From 22d5272915c4ee5d9278f116fa94b400e083ddc6 Mon Sep 17 00:00:00 2001 From: SangHyoun Yun Date: Sun, 10 Jun 2018 01:02:04 +0900 Subject: [PATCH 0001/1795] Replaced README.korean.md contents with README.md - The language of README.korean.md was Chinese, Replaced README.korean.md contents with latest version of README.md --- README.korean.md | 688 ++++++++++++++++++++++++----------------------- 1 file changed, 350 insertions(+), 338 deletions(-) diff --git a/README.korean.md b/README.korean.md index cb14a8354..89a2354ec 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1,352 +1,349 @@ [✔]: assets/images/checkbox-small-blue.png -# Node.js 最佳实践 +# Node.js Best Practices

- Node.js Best Practices + Node.js Best Practices


-53 items Last update: Nov 15, 2017 Updated for Node v.8.9 -
+ 53 items Last update: Apr 23, 2018 Updated for Node v.8.11 +
- [![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) -
+[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) -# 欢迎! 首先您应该知道的三件事情: -**1. 当您读到这里,实际上您读了很多关于Node.js的优秀文章 -** 这是对Node.js最佳实践中排名最高的内容的总结和分享 +
+ +Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR**, ![RU](/assets/flags/RU.png)**RU** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) + +
+ +# Welcome! 3 Things You Ought To Know First: -**2. 这里是最大的汇集,且每周都在增长 -** 当前,超过50个最佳实现,样式指南,架构建议已经呈现。每天都有新的issue和PR被创建,以使这本在线书籍不断更新。我们很乐于见到您能在这里做出贡献,不管是修复一些代码的错误,或是提出绝妙的新想法。请查看我们的[milestones](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) +**1. When you read here, you in fact read dozens of the best Node.js articles -** this is a summary and curation of the top-ranked content on Node.js best practices -**3. 大部分的条目包含额外的信息 -** 大部分的最佳实践条目的旁边,您将发现 **🔗Read More** 链接,它将呈现给您示例代码,博客引用和更多信息 +**2. It is the largest compilation, and it is growing every week -** currently, more than 50 best practices, style guides, and architectural tips are presented. New issues and PR are created every day to keep this live book updated. We'd love to see you contributing here, whether fixing some code mistake or suggesting brilliant new ideas. See our [milestones here](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) + +**3. Most bullets have additional info -** nearby most best practice bullets you'll find **🔗Read More** link that will present you with code examples, quotes from selected blogs and more info


-## 目录 -1. [项目结构实践 (5) ](#1-project-structure-practices) -2. [异常处理实践 (11) ](#2-error-handling-practices) -3. [编码规范实践 (12) ](#3-code-style-practices) -4. [测试和总体质量实践 (8) ](#4-testing-and-overall-quality-practices) -5. [进入生产实践 (16) ](#5-going-to-production-practices) -6. Security Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) -7. Performance Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) +## Table of Contents +1. [Project structure Practices (5)](#1-project-structure-practices) +2. [Error Handling Practices (11) ](#2-error-handling-practices) +3. [Code Style Practices (12) ](#3-code-style-practices) +4. [Testing And Overall Quality Practices (8) ](#4-testing-and-overall-quality-practices) +5. [Going To Production Practices (17) ](#5-going-to-production-practices) +6. Security Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) +7. Performance Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open))


-

1. 项目结构实践

-## ![✔] 1.1 组件式构建你的解决方案 +# `1. Project Structure Practices` + +## ![✔] 1.1 Structure your solution by components - **TL;DR:** 大型项目的最坏的隐患就是维护一个庞大的,含有几百个依赖的代码库 - 当开发人员准备整合新的需求的时候,这样一个庞然大物势必减缓了开发效率。反之,把您的代码拆分成组件,每一个组件有它自己的文件夹和代码库,并且确保每一个组件小而简单。查看正确的项目结构的例子请访问下面的 ‘更多’ 链接。 +**TL;DR:** The worst large applications pitfall is maintaining a huge code base with hundreds of dependencies - such a monolith slows down developers as they try to incorporate new features. Instead, partition your code into components, each gets its own folder or a dedicated codebase, and ensure that each unit is kept small and simple. Visit 'Read More' below to see examples of correct project structure -**否则:** 当编写新需求的开发人员逐步意识到他所做改变的影响,并担心会破坏其他的依赖模块 - 部署会变得更慢,风险更大。当所有业务逻辑没有被分开,这也会被认为很难扩展 +**Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependant components - deployments become slower and more risky. It's also considered harder to scale-out when all the business units are not separated -🔗 [**Read More: structure by components**](/sections/projectstructre/breakintcomponents.korean.md) +🔗 [**Read More: structure by components**](/sections/projectstructre/breakintcomponents.md)

-## ![✔] 1.2 分层设计组件,保持Express在特定的区域 +## ![✔] 1.2 Layer your components, keep Express within its boundaries -**TL;DR:** 每一个组件都应该包含'层级' - 一个专注的用于接入网络,逻辑,数据的概念。这样不仅获得一个清晰的分离考量,而且使仿真和测试系统变得异常容易。尽管这是一个普通的模式,但接口开发者易于混淆层级关系,比如把网络层的对象(Express req, res)传给业务逻辑和数据层 - 这会令您的应用彼此依赖,并且只能通过Express使用。 +**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (Express req, res) to business logic and data layers - this makes your application dependant on and accessible by Express only -**否则:** 对于混淆了网络层和其它层的应用,将不易于测试,执行CRON的任务,其它非-Express的调用者无法使用 +**Otherwise:** App that mixes web objects with other layers can not be accessed by testing code, CRON jobs and other non-Express callers -🔗 [**更多: 应用分层**](/sections/projectstructre/createlayers.chinese.md) +🔗 [**Read More: layer your app**](/sections/projectstructre/createlayers.md)

-## ![✔] 1.3 封装公共模块成为NPM的包 +## ![✔] 1.3 Wrap common utilities as NPM packages -**TL;DR:** 由大量代码构成的一个大型应用中,贯彻全局的,比如日志,加密和其它类似的公共组件,应该进行封装,并暴露成一个私有的NPM包。这将使其在更多的代码库和项目中被使用变成了可能。 +**TL;DR:** In a large app that constitutes a large code base, cross-cutting-concern utilities like logger, encryption and alike, should be wrapped by your own code and exposed as private NPM packages. This allows sharing them among multiple code bases and projects -**否则:** 您将不得不重造部署和依赖的轮子 +**Otherwise:** You'll have to invent your own deployment and dependency wheel -🔗 [**更多: 通过需求构建**](/sections/projectstructre/wraputilities.chinese.md) +🔗 [**Read More: Structure by feature**](/sections/projectstructre/wraputilities.md)

-## ![✔] 1.4 分离 Express 'app' and 'server' +## ![✔] 1.4 Separate Express 'app' and 'server' -**TL;DR:** 避免定义整个[Express](https://expressjs.com/)应用在一个单独的大文件里, 这是一个不好的习惯 - 分离您的 'Express' 定义至少在两个文件中: API声明(app.js) 和 网络相关(WWW)。对于更好的结构,是把你的API声明放在组件中。 +**TL;DR:** Avoid the nasty habit of defining the entire [Express](https://expressjs.com/) app in a single huge file - separate your 'Express' definition to at least two files: the API declaration (app.js) and the networking concerns (WWW). For even better structure, locate your API declaration within components -**否则:** 您的API将只能通过HTTP的调用进行测试(慢,并且很难产生测试覆盖报告)。维护一个有着上百行代码的文件也不是一个令人开心的事情。 +**Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file -🔗 [**更多: 分离 Express 'app' and 'server'**](/sections/projectstructre/separateexpress.chinese.md) +🔗 [**Read More: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.md)

-## ![✔] 1.5 使用易于设置环境变量,安全和分级的配置 - +## ![✔] 1.5 Use environment aware, secure and hierarchical config -**TL;DR:** 一个完美无瑕的配置安装应该确保 (a) 元素可以从文件中,也可以从环境变量中读取 (b) 密码排除在提交的代码之外 (c) 为了易于检索,配置是分级的。仅有几个包可以满足这样的条件,比如[rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) 和 [config](https://www.npmjs.com/package/config)。 +**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) -**否则:** 不能满足任意的配置要求将会使开发,运维团队,或者两者,易于陷入泥潭。 +**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both -🔗 [**更多: 配置最佳实践**](/sections/projectstructre/configguide.chinese.md) +🔗 [**Read More: configuration best practices**](/sections/projectstructre/configguide.md)


-

⬆ 返回顶部

+

⬆ Return to top

-

2. 错误处理最佳实践

+# `2. Error Handling Practices` -## ![✔] 2.1 使用 Async-Await 和 promises 用于异步错误处理 +## ![✔] 2.1 Use Async-Await or promises for async error handling -**TL;DR:** 使用回调的方式处理异步错误可能是导致灾难的最快的方式(a.k.a the pyramid of doom)。对您的代码来说,最好的礼物就是使用规范的promise库或async-await来替代,这会使其像try-catch一样更加简洁,具有熟悉的代码结构。 +**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using a reputable promise library or async-await instead which enables a much more compact and familiar code syntax like try-catch -**否则:** Node.js回调特性, function(err, response), 是导致不可维护代码的一个必然的方式。究其原因,是由于混合了随意的错误处理代码,臃肿的内嵌,蹩脚的代码模式。 +**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting and awkward coding patterns -🔗 [**更多: 避免回调**](/sections/errorhandling/asyncerrorhandling.chinese.md) +🔗 [**Read More: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.md)

-## ![✔] 2.2 仅使用内建的错误对象 +## ![✔] 2.2 Use only the built-in Error object -**TL;DR:** 很多人抛出异常使用字符串类型或一些自定义类型 - 这会导致错误处理逻辑和模块间的调用复杂化。是否您reject一个promise,抛出异常或发出(emit)错误 - 使用内建的错误对象将会增加设计一致性,并防止信息的丢失。 +**TL;DR:** Many throws errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Whether you reject a promise, throw an exception or an emit error – using only the built-in Error object will increase uniformity and prevent loss of information +**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! -**否则:** 调用某些模块,将不确定哪种错误类型会返回 - 这将会使恰当的错误处理更加困难。更坏的情况是,使用特定的类型描述错误,会导致重要的错误信息缺失,比如stack trace! - -🔗 [**更多: 使用内建错误对象**](/sections/errorhandling/useonlythebuiltinerror.chinese.md) +🔗 [**Read More: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.md)

-## ![✔] 2.3 区分运行错误和程序设计错误 +## ![✔] 2.3 Distinguish operational vs programmer errors -**TL;DR:** 运行错误(例如, API接受到一个无效的输入)指的是一些已知场景下的错误,这类错误的影响已经完全被理解,并能被考虑周全的处理掉。同时,程序设计错误(例如,尝试读取未定义的变量)指的是未知的编码问题,影响到应用得当的重启。 +**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, programmer error (e.g. trying to read undefined variable) refers to unknown code failures that dictate to gracefully restart the application -**否则:** 当一个错误产生的时候,您总是得重启应用,但为什么要让 ~5000 个在线用户不能访问,仅仅是因为一个细微的,可以预测的,运行时错误?相反的方案,也不完美 – 当未知的问题(程序问题)产生的时候,使应用依旧可以访问,可能导致不可预测行为。区分两者会使处理更有技巧,并在给定的上下文下给出一个平衡的对策。 +**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context -🔗 [**更多: 运行错误和程序设计错误**](/sections/errorhandling/operationalvsprogrammererror.chinese.md) +🔗 [**Read More: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.md)

-## ![✔] 2.4 集中处理错误,不要在Express中间件中处理错误 +## ![✔] 2.4 Handle errors centrally, not within an Express middleware -**TL;DR:** 错误处理逻辑,比如给管理员发送邮件,日志应该封装在一个特定的,集中的对象当中,这样当错误产生的时候,所有的终端(例如 Express中间件,cron任务,单元测试)都可以调用。 +**TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in -**否则:** 错误处理的逻辑不放在一起将会导致代码重复和非常可能不恰当的错误处理。 +**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors -🔗 [**更多: 集中处理错误**](/sections/errorhandling/centralizedhandling.chinese.md) +🔗 [**Read More: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.md)

-## ![✔] 2.5 对API错误使用Swagger文档化 - -**TL;DR:** 让你的API调用者知道哪种错误会返回,这样他们就能完全的处理这些错误,而不至于系统崩溃。Swagger,REST API的文档框架,通常处理这类问题。 +## ![✔] 2.5 Document API errors using Swagger -**否则:** 任何API的客户端可能决定崩溃并重启,仅仅因为它收到一个不能处理的错误。注意:API的调用者可能是你(在微服务环境中非常典型)。 +**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. This is usually done with REST API documentation frameworks like Swagger +**Otherwise:** An API client might decide to crash and restart only because he received back an error he couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) -🔗 [**更多: 使用Swagger记录错误**](/sections/errorhandling/documentingusingswagger.chinese.md) +🔗 [**Read More: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.md)

-## ![✔] 2.6 当一个特殊的情况产生,停掉服务是得体的 +## ![✔] 2.6 Shut the process gracefully when a stranger comes to town -**TL;DR:** 当一个不确定错误产生(一个开发错误,最佳实践条款#3) - 这就意味着对应用运转健全的不确定。一个普通的实践将是建议仔细地重启进程,并使用一些‘启动器’工具,比如Forever和PM2。 +**TL;DR:** When an unknown error occurs (a developer error, see best practice number #3)- there is uncertainty about the application healthiness. A common practice suggests restarting the process carefully using a ‘restarter’ tool like Forever and PM2 -**否则:** 当一个未知的异常被抛出,意味着某些对象包含错误的状态(例如某个全局事件发生器由于某些内在的错误,不在产生事件),未来的请求可能失败或者行为异常。 +**Otherwise:** When an unfamiliar exception is caught, some object might be in a faulty state (e.g an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily -🔗 [**更多: 停掉服务**](/sections/errorhandling/shuttingtheprocess.chinese.md) +🔗 [**Read More: shutting the process**](/sections/errorhandling/shuttingtheprocess.md)

+## ![✔] 2.7 Use a mature logger to increase error visibility +**TL;DR:** A set of mature logging tools like Winston, Bunyan or Log4J, will speed-up error discovery and understanding. So forget about console.log -## ![✔] 2.7 使用一个成熟的日志工具提高错误的可见性 - -**TL;DR:** 一系列成熟的日志工具,比如Winston,Bunyan和Log4J,会加速错误的发现和理解。忘记console.log吧。 - -**否则:** 浏览console的log,和不通过查询工具或者一个好的日志查看器,手动浏览繁琐的文本文件,会使你忙于工作到很晚。 - -🔗 [**更多: 使用好用的日志工具**](/sections/errorhandling/usematurelogger.chinese.md) +**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late +🔗 [**Read More: using a mature logger**](/sections/errorhandling/usematurelogger.md)

+## ![✔] 2.8 Test error flows using your favorite test framework -## ![✔] 2.8 使用你最喜欢的测试框架测试错误流 - -**TL;DR:** 无论专业的自动化测试或者简单的手动开发测试 - 确保您的代码不仅满足正常的场景,而且处理并且返回正确的错误。测试框架,比如Mocha & Chai可以非常容易的处理这些问题(在"Gist popup"中查看代码实例) 。 - -**否则:** 没有测试,不管自动还是手动,您不可能依赖代码去返回正确的错误。而没有可以理解的错误,那将毫无错误处理可言。 +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenario but also handle and return the right errors. Testing frameworks like Mocha & Chai can handle this easily (see code examples within the "Gist popup") +**Otherwise:** Without testing, whether automatically or manually, you can’t rely on our code to return the right errors. Without meaningful errors – there’s no error handling -🔗 [**更多: 测试错误流向**](/sections/errorhandling/testingerrorflows.chinese.md) +🔗 [**Read More: testing error flows**](/sections/errorhandling/testingerrorflows.md)

-## ![✔] 2.9 使用APM产品发现错误和宕机时间 +## ![✔] 2.9 Discover errors and downtime using APM products -**TL;DR:** 监控和性能产品 (别名 APM) 先前一步的检测您的代码库和API,这样他们能自动的,像使用魔法一样的强调错误,宕机和您忽略的性能慢的部分。 +**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes and slow parts that you were missing -**否则:** 您花了很多的力气在测量API的性能和错误,但可能您从来没有意识到真实场景下您最慢的代码块和他们对UX的影响。 +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX - -🔗 [**更多: 使用APM产品**](/sections/errorhandling/apmproducts.chinese.md) +🔗 [**Read More: using APM products**](/sections/errorhandling/apmproducts.md)

+## ![✔] 2.10 Catch unhandled promise rejections -## ![✔] 2.10 捕获未处理的promise rejections - -**TL;DR:** 任何在promise中被抛出的异常将被收回和遗弃,除非开发者没有忘记去明确的处理。即使您的代码调用的是process.uncaughtException!解决这个问题可以注册到事件process.unhandledRejection。 - -**否则:** 您的错误将被回收,无踪迹可循。没有什么可以需要考虑。 +**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle. Even if your code is subscribed to process.uncaughtException! Overcome this by registering to the event process.unhandledRejection +**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about -🔗 [**更多: 捕获未处理的promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.chinese.md) +🔗 [**Read More: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.md)

-## ![✔] 2.11 快速查错,验证参数使用一个专门的库Fail fast, validate arguments using a dedicated library +## ![✔] 2.11 Fail fast, validate arguments using a dedicated library -**TL;DR:** 这应该是您的Express最佳实践中的一部分 – assert API输入避免难以理解的漏洞,这类漏洞以后会非常难以追踪。而验证代码通常是一件乏味的事情,除非使用一些非常炫酷的帮助库比如Joi。 +**TL;DR:** This should be part of your Express best practices – Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like Joi -**否则:** 考虑这种情况 – 您的功能期望一个数字参数 “Discount” ,然而调用者忘记传值,之后在您的代码中检查是否 Discount!=0 (允许的折扣值大于零),这样它将允许用户使用一个折扣。OMG,多么不爽的一个漏洞。你能明白吗? +**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? -🔗 [**更多: 快速查错**](/sections/errorhandling/failfast.chinese.md) +🔗 [**Read More: failing fast**](/sections/errorhandling/failfast.md)


-

⬆ 返回顶部

+

⬆ Return to top

-

3. 编码风格实践

+# `3. Code Style Practices` -## ![✔] 3.1 使用ESLint +## ![✔] 3.1 Use ESLint -**TL;DR:** [ESLint](https://eslint.org)是检查可能的代码错误和修复代码样式的事实上的标准,不仅可以识别实际的间距问题, 而且还可以检测严重的反模式代码, 如开发人员在不分类的情况下抛出错误。尽管ESlint可以自动修复代码样式,但其他的工具比如[prettier](https://www.npmjs.com/package/prettier)和[beautify](https://www.npmjs.com/package/js-beautify)在格式化修复上功能强大,可以和Eslint结合起来使用。 +**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint -**否则:** 开发人员将必须关注单调乏味的间距和线宽问题, 并且时间可能会浪费在过多考虑项目的代码样式。 +**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking about the project's code style

-## ![✔] 3.2 Node.js特定的插件 +## ![✔] 3.2 Node.js Specific Plugins -**TL;DR:** 除了仅仅涉及 vanilla JS 的 ESLint 标准规则,添加 Node 相关的插件,比如[eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) +**TL;DR:** On top of ESLint standard rules that cover vanilla JS only, add Node-specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) -**否则:** 许多错误的Node.js代码模式可能在检测下逃生。例如,开发人员可能需要某些文件,把一个变量作为路径名 (variableAsPath) ,这会导致攻击者可以执行任何JS脚本。Node.JS linters可以检测这类模式,并及早预警。 +**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early

-## ![✔] 3.3 在同一行开始一个代码块的大括号 +## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line -**TL;DR:** 代码块的第一个大括号应该和声明的起始保持在同一行中。 +**TL;DR:** The opening curly braces of a code block should be in the same line of the opening statement + +### Code Example -### 代码示例 ```javascript - // 建议 - function someFunction() { - // 代码块 - } - - // 避免 - function someFunction - { - // 代码块 - } +// Do +function someFunction() { + // code block +} + +// Avoid +function someFunction() +{ + // code block +} ``` -**否则:** 不遵守这项最佳实践可能导致意外的结果,在Stackoverflow的帖子中可以查看到,如下: +**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: -🔗 [**更多:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) +🔗 [**Read more:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

-## ![✔] 3.4 不要忘记分号 +## ![✔] 3.4 Don't Forget the Semicolon -**TL;DR:** 即使没有获得一致的认同,但在每一个表达式后面放置分号还是值得推荐的。这将使您的代码, 对于其他阅读代码的开发者来说,可读性,明确性更强。 +**TL;DR:** While not unanimously agreed upon, it is still recommended to put a semicolon at the end of each statement. This will make your code more readable and explicit to other developers who read it -**否则:** 在前面的章节里面已经提到,如果表达式的末尾没有添加分号,JavaScript的解释器会在自动添加一个,这可能会导致一些意想不到的结果。 +**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one which might lead to some undesired results

-## ![✔] 3.5 命名您的方法 +## ![✔] 3.5 Name Your Functions -**TL;DR:** 命名所有的方法,包含闭包和回调, 避免匿名方法。当剖析一个node应用的时候,这是特别有用的。命名所有的方法将会使您非常容易的理解内存快照中您正在查看的内容。 +**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot -**否则:** 使用一个核心dump(内存快照)调试线上问题,会是一项非常挑战的事项,因为你注意到的严重内存泄漏问题极有可能产生于匿名的方法。 +**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions

-## ![✔] 3.6 变量、常量、函数和类的命名约定 +## ![✔] 3.6 Naming conventions for variables, constants, functions and classes + +**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions and **_UpperCamelCase_** (capital first letter as well) when naming classes. This will help you to easily distinguish between plain variables/functions, and classes that require instantiation. Use descriptive names, but try to keep them short -**TL;DR:** 当命名变量和方法的时候,使用 ***lowerCamelCase*** ,当命名类的时候,使用 ***UpperCamelCase*** (首字母大写),对于常量,则 ***UPPERCASE*** 。这将帮助您轻松地区分普通变量/函数和需要实例化的类。使用描述性名称,但使它们尽量简短。 +**Otherwise:** Javascript is the only language in the world which allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase -**否则:** JavaScript是世界上唯一一门不需要实例化,就可以直接调用构造函数("Class")的编码语言。因此,类和函数的构造函数由采用UpperCamelCase开始区分。 +### Code Example -### 代码示例 ### ```javascript - // 使用UpperCamelCase命名类名 - class SomeClassExample () { - - // 常量使用const关键字,并使用lowerCamelCase命名 - const config = { - key: 'value' - }; - - // 变量和方法使用lowerCamelCase命名 - let someVariableExample = 'value'; - function doSomething() { - - } - - } +// for class name we use UpperCamelCase +class SomeClassExample {} + +// for const names we use the const keyword and lowerCamelCase +const config = { + key: 'value' +}; + +// for variables and functions names we use lowerCamelCase +let someVariableExample = 'value'; +function doSomething() {} ```

-## ![✔] 3.7 使用const优于let,废弃var +## ![✔] 3.7 Prefer const over let. Ditch the var -**TL;DR:** 使用`const`意味着一旦一个变量被分配,它不能被重新分配。使用const将帮助您免于使用相同的变量用于不同的用途,并使你的代码更清晰。如果一个变量需要被重新分配,以在一个循环为例,使用`let`声明它。let的另一个重要方面是,使用let声明的变量只在定义它的块作用域中可用。 `var`是函数作用域,不是块级作用域,既然您有const和let让您随意使用,那么[不应该在ES6中使用var](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70)。 +**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring const will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have const and let at your disposal -**否则:** 当经常更改变量时,调试变得更麻烦了。 +**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes -🔗 [**更多: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) +🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75)

-## ![✔] 3.8 先require, 而不是在方法内部 +## ![✔] 3.8 Requires come first, and not inside functions -**TL;DR:** 在每个文件的起始位置,在任何函数的前面和外部require模块。这种简单的最佳实践,不仅能帮助您轻松快速地在文件顶部辨别出依赖关系,而且避免了一些潜在的问题。 +**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems -**否则:** 在Node.js中,require 是同步运行的。如果从函数中调用它们,它可能会阻塞其他请求,在更关键的时间得到处理。另外,如果所require的模块或它自己的任何依赖项抛出错误并使服务器崩溃,最好尽快查明它,如果该模块在函数中require的,则可能不是这样的情况。 +**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its own dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function

-## ![✔] 3.9 在文件夹上 require ,而不是直接在文件上 +## ![✔] 3.9 Do Require on the folders, not directly on the files + +**TL;DR:** When developing a module/library in a folder, place an index.js file that exposes the module's +internals so every consumer will pass through it. This serves as an 'interface' to your module and eases +future changes without breaking the contract -**TL;DR:** 当在一个文件夹中开发库/模块,放置一个文件index.js暴露模块的 -内部,这样每个消费者都会通过它。这将作为您模块的一个接口,并使未来的变化简单而不违反规则。 +**Otherwise:** Changing the internal structure of files or the signature may break the interface with +clients -**否则:** 更改文件内部结构或签名可能会破坏与客户端的接口。 +### Code example -### 代码示例 ```javascript - // 建议 - module.exports.SMSProvider = require('./SMSProvider'); - module.exports.SMSNumberResolver = require('./SMSNumberResolver'); +// Do +module.exports.SMSProvider = require('./SMSProvider'); +module.exports.SMSNumberResolver = require('./SMSNumberResolver'); - // 避免 - module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js'); - module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js'); +// Avoid +module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js'); +module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js'); ```

+## ![✔] 3.10 Use the `===` operator -## ![✔] 3.10 使用 `===` 操作符 +**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal -**TL;DR:** 对比弱等于 `==`,优先使用严格的全等于 `===` 。`==`将在它们转换为普通类型后比较两个变量。在 `===` 中没有类型转换,并且两个变量必须是相同的类型。 +**Otherwise:** Unequal variables might return true when compared with the `==` operator -**否则:** 与 `==` 操作符比较,不相等的变量可能会返回true。 +### Code example -### 代码示例 ```javascript '' == '0' // false 0 == '' // true @@ -361,360 +358,367 @@ null == undefined // true ' \t\r\n ' == 0 // true ``` -如果使用`===`, 上面所有语句都将返回 false。 + +All statements above will return false if used with `===`

-## ![✔] 3.11 使用 Async Await, 避免回调 +## ![✔] 3.11 Use Async Await, avoid callbacks -**TL;DR:** Node 8 LTS现已全面支持异步等待。这是一种新的方式处理异步请求,取代回调和promise。Async-await是非阻塞的,它使异步代码看起来像是同步的。您可以给你的代码的最好的礼物是用async-await提供了一个更紧凑的,熟悉的,类似try catch的代码语法。 +**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch -**否则:** 使用回调的方式处理异步错误可能是陷入困境最快的方式 - 这种方式必须面对不停地检测错误,处理别扭的代码内嵌,难以推理编码流。 +**Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and make it difficult to reason about the code flow -🔗[**更多:** async await 1.0 引导](https://github.com/yortus/asyncawait) +🔗[**Read more:** Guide to async await 1.0](https://github.com/yortus/asyncawait)

-## ![✔] 3.12 使用 (=>) 箭头函数 - -**TL;DR:** 尽管使用 async-await 和避免方法作为参数是被推荐的, 但当处理那些接受promise和回调的老的API的时候 - 箭头函数使代码结构更加紧凑,并保持了根方法上的语义上下文 (例如 'this')。 +## ![✔] 3.12 Use Fat (=>) Arrow Functions -**否则:** 更长的代码(在ES5方法中)更易于产生缺陷,并读起来很是笨重。 +**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older API that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. 'this') -🔗 [**更多: 这是拥抱箭头函数的时刻**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) +**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read +🔗 [**Read mode: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75)


-

⬆ 返回顶部

+

⬆ Return to top

+# `4. Testing And Overall Quality Practices` -

4. 测试和总体的质量实践

+## ![✔] 4.1 At the very least, write API (component) testing -## ![✔] 4.1 至少,编写API(组件)测试 +**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' run out of control and being abandoned. For that reason, prioritize and start with API testing which is the easiest to write and provide more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/). Afterward, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc -**TL;DR:** 大多数项目只是因为时间表太短而没有进行任何自动化测试,或者测试项目失控而正被遗弃。因此,优先从API测试开始,这是最简单的编写和提供比单元测试更多覆盖率的事情(你甚至可能不需要编码而进行API测试,像[Postman](https://www.getpostman.com/)。之后,如果您有更多的资源和时间,继续使用高级测试类型,如单元测试、DB测试、性能测试等。 - -**否则:** 您可能需要花很长时间编写单元测试,才发现只有20%的系统覆盖率。 +**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage

-## ![✔] 4.2 使用一个linter检测代码问题 - -**TL;DR:** 使用代码linter检查基本质量并及早检测反模式。在任何测试之前运行它, 并将其添加为预提交的git钩子, 以最小化审查和更正任何问题所需的时间。也可在[Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices)中查阅编码样式实践 +## ![✔] 4.2 Detect code issues with a linter -**否则:** 您可能让一些反模式和易受攻击的代码传递到您的生产环境中。 +**TL;DR:** Use a code linter to check basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices) on Code Style Practices +**Otherwise:** You may let pass some anti-pattern and possible vulnerable code to your production environment.

-## ![✔] 4.3 仔细挑选您的持续集成(CI)平台 +## ![✔] 4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) -**TL;DR:** 您的持续集成平台(cicd)将集成各种质量工具(如测试、lint),所以它应该是一个充满活力的生态系统,包含各种插件。[jenkins](https://jenkins.io/)曾经是许多项目的默认选项,因为它有最大的社区,同时也是一个非常强大的平台,这样的代价是要求一个陡峭的学习曲线。如今,使用SaaS工具,比如[CircleCI](https://circleci.com)及其他,安装一套CI解决方案,相对是一件容易的事情。这些工具允许构建灵活的CI管道,而无需管理整个基础设施。最终,这是一个鲁棒性和速度之间的权衡 - 仔细选择您支持的方案。 +**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of complex setup that demands a steep learning curve. Nowadays, it became much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully -**否则:** 一旦您需要一些高级定制,选择一些细分市场供应商可能会让您停滞不前。另一方面,伴随着jenkins,可能会在基础设施设置上浪费宝贵的时间。 +**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup -🔗 [**更多: 挑选 CI 平台**](/sections/testingandquality/citools.chinese.md) +🔗 [**Read More: Choosing CI platform**](/sections/testingandquality/citools.md)

-## ![✔] 4.4 经常检查易受攻击的依赖 +## ![✔] 4.4 Constantly inspect for vulnerable dependencies -**TL;DR:** 即使是那些最有名的依赖模块,比如Express,也有已知的漏洞。使用社区和商业工具,比如 🔗 [nsp](https://github.com/nodesecurity/nsp) ,集成在您的CI平台上,在每一次构建的时候都会被调用,这样可以很容易地解决漏洞问题。 +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [nsp](https://github.com/nodesecurity/nsp) that can be invoked from your CI on every build -**否则:** 在没有专用工具的情况下,使代码清除漏洞,需要不断地跟踪有关新威胁的在线出版物,相当繁琐。 +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious

-## ![✔] 4.5 测试标签化 +## ![✔] 4.5 Tag your tests -**TL;DR:** 不同的测试必须运行在不同的情景:quick smoke,IO-less,当开发者保存或提交一个文件,测试应该启动;完整的端到端的测试通常运行在一个新的pull request被提交之后,等等。这可以通过对测试用例设置标签,比如关键字像#cold #api #sanity,来完成。这样您可以对您的测试集进行grep,调用需要的子集。例如,这就是您通过[Mocha](https://mochajs.org/)仅仅调用sanity测试集所需要做的:mocha --grep 'sanity'。 +**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' -**否则:** 运行所有的测试,包括执行数据库查询的几十个测试,任何时候开发者进行小的改动都可能很慢,这使得开发者不愿意运行测试。 +**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests

-## ![✔] 4.6 检查测试覆盖率,它有助于识别错误的测试模式 - -**TL;DR:** 代码覆盖工具比如[Istanbul/NYC ](https://github.com/gotwarlost/istanbul),很好用有3个原因:它是免费的(获得这份报告不需要任何开销),它有助于确定测试覆盖率降低的部分,以及最后但非最不重要的是它指出了测试中的不匹配:通过查看颜色标记的代码覆盖报告您可以注意到,例如,从来不会被测到的代码片段像catch语句(即测试只是调用正确的路径,而不调用应用程序发生错误时的行为)。如果覆盖率低于某个阈值,则将其设置为失败的构建。 - -**否则:** 当你的大部分代码没有被测试覆盖时,就不会有任何自动化的度量指标告诉你了。 +## ![✔] 4.6 Check your test coverage, it helps to identify wrong test patterns +**TL;DR:** Code coverage tools like [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold +**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing

-## ![✔] 4.7 检查过期的依赖包 +## ![✔] 4.7 Inspect for outdated packages -**TL;DR:** 使用您的首选工具 (例如 “npm outdated” or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) 来检测已安装的过期依赖包, 将此检查注入您的 CI 管道, 甚至在严重的情况下使构建失败。例如, 当一个已安装的依赖包滞后5个补丁时 (例如:本地版本是1.3.1 的, 存储库版本是1.3.8 的), 或者它被其作者标记为已弃用, 可能会出现严重的情况 - 停掉这次构建并防止部署此版本。 +**TL;DR:** Use your preferred tool (e.g. 'npm outdated' or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) to detect installed packages which are outdated, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version -**否则:** 您的生产环境将运行已被其作者明确标记为有风险的依赖包 +**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky

-## ![✔] 4.8 对于e2e testing,使用docker-compose - -**TL;DR:** 端对端(e2e)测试包含现场数据,由于它依赖于很多重型服务如数据库,习惯被认为是CI过程中最薄弱的环节。Docker-compose通过制定类似生产的环境,并使用一个简单的文本文件和简单的命令,轻松化解了这个问题。它为了e2e测试,允许制作所有相关服务,数据库和隔离网络。最后但并非最不重要的一点是,它可以保持一个无状态环境,该环境在每个测试套件之前被调用,然后立即消失。 +## ![✔] 4.8 Use docker-compose for e2e testing +**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Docker-compose turns this problem into a breeze by crafting production-like environment using a simple text file and easy commands. It allows crafting all the dependent services, DB and isolated network for e2e testing. Last but not least, it can keep a stateless environment that is invoked before each test suite and dies right after -**否则:** 没有docker-compose,团队必须维护一个测试数据库在每一个测试环境上,包含开发机器,保持所有数据同步,这样测试结果不会因环境不同而不同。 - +**Otherwise:** Without docker-compose teams must maintain a testing DB for each testing environment including developers machines, keep all those DBs in sync so test results won't vary across environments


-

⬆ 返回顶部

- -

5. 上线实践

+

⬆ Return to top

-## ![✔] 5.1. 监控! +# `5. Going To Production Practices` -**TL;DR:** 监控是一种在顾客之前发现问题的游戏 – 显然这应该被赋予前所未有的重要性。考虑从定义你必须遵循的基本度量标准开始(我的建议在里面),到检查附加的花哨特性并选择解决所有问题的解决方案。市场已经淹没其中。点击下面的 ‘The Gist’ ,了解解决方案的概述。 +## ![✔] 5.1. Monitoring! -**否则:** 错误 === 失望的客户. 非常简单. +**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions +**Otherwise:** Failure === disappointed customers. Simple -🔗 [**更多: 监控!**](/sections/production/monitoring.chinese.md) +🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md)

-## ![✔] 5.2. 使用智能日志增加透明度 +## ![✔] 5.2. Increase transparency using smart logging -**TL;DR:** 日志可以是调试语句的一个不能说话的仓库,或者表述应用运行过程的一个漂亮仪表板的驱动。从第1天计划您的日志平台:如何收集、存储和分析日志,以确保所需信息(例如,错误率、通过服务和服务器等完成整个事务)都能被提取出来。 +**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted -**否则:** 您最终像是面对一个黑盒,不知道发生了什么事情,然后你开始重新写日志语句添加额外的信息。 +**Otherwise:** You end-up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md) -🔗 [**更多: 使用智能日志增加透明度**](/sections/production/smartlogging.chinese.md) -

-## ![✔] 5.3. 委托一切可能的(例如:gzip,SSL)给反向代理 - -**TL;DR:** Node处理CPU密集型任务,如gzipping,SSL termination等,表现糟糕。相反,使用一个 ‘真正’ 的中间件服务像Nginx,HAProxy或者云供应商的服务。 +## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy -**否则:** 可怜的单线程Node将不幸地忙于处理网络任务,而不是处理应用程序核心,性能会相应降低。 +**TL;DR:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead +**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly -🔗 [**更多: 委托一切可能的(例如:gzip,SSL)给反向代理**](/sections/production/delegatetoproxy.chinese.md) +🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md)

-## ![✔] 5.4. 锁住依赖 +## ![✔] 5.4. Lock dependencies -**TL;DR:** 您的代码必须在所有的环境中是相同的,但是令人惊讶的是,NPM默认情况下会让依赖在不同环境下发生偏移 – 当在不同的环境中安装包的时候,它试图拿包的最新版本。克服这种问题可以利用NPM配置文件, .npmrc,告诉每个环境保存准确的(不是最新的)包的版本。另外,对于更精细的控制,使用NPM “shrinkwrap”。*更新:作为NPM5,依赖默认锁定。新的包管理工具,Yarn,也默认锁定。 +**TL;DR:** Your code must be identical across all environments, but amazingly NPM lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using NPM config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grain control use NPM” shrinkwrap”. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default -**否则:** QA测试通过的代码和批准的版本,在生产中表现不一致。更糟糕的是,同一生产集群中的不同服务器可能运行不同的代码。 +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently at production. Even worse, different servers at the same production cluster might run different code - -🔗 [**更多: 锁住依赖**](/sections/production/lockdependencies.chinese.md) +🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md)

-## ![✔] 5.5. 使用正确的工具保护进程正常运行 - -**TL;DR:** 进程必须继续运行,并在失败时重新启动。对于简单的情况下,“重启”工具如PM2可能足够,但在今天的“Dockerized”世界 – 集群管理工具也值得考虑 +## ![✔] 5.5. Guard process uptime using the right tool -**否则:** 运行几十个实例没有明确的战略和太多的工具(集群管理,docker,PM2)可能导致一个DevOps混乱 +**TL;DR:** The process must go on and get restarted upon failures. For simple scenarios, ‘restarter’ tools like PM2 might be enough but in today ‘dockerized’ world – a cluster management tools should be considered as well +**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to a DevOps chaos -🔗 [**更多: 使用正确的工具保护进程正常运行**](/sections/production/guardprocess.chinese.md) +🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md) -

-## ![✔] 5.6. 利用CPU多核 +## ![✔] 5.6. Utilize all CPU cores -**TL;DR:** 在基本形式上,node应用程序运行在单个CPU核心上,而其他都处于空闲状态。复制node进程和利用多核,这是您的职责 – 对于中小应用,您可以使用Node Cluster和PM2. 对于一个大的应用,可以考虑使用一些Docker cluster(例如k8s,ECS)复制进程或基于Linux init system(例如systemd)的部署脚本 +**TL;DR:** At its basic form, a Node app runs on a single CPU core while all other are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) -**否则:** 您的应用可能只是使用了其可用资源中的25% (!),甚至更少。注意,一台典型的服务器有4个或更多的CPU,默认的Node.js部署仅仅用了一个CPU(甚至使用PaaS服务,比如AWS beanstalk,也一样)。 +**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) - -🔗 [**更多: 利用所有的CPU**](/sections/production/utilizecpu.chinese.md) +🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md)

-## ![✔] 5.7. 创建一个“维护端点” - -**TL;DR:** 在一个安全的API中暴露一组系统相关的信息,比如内存使用情况和REPL等等。尽管这里强烈建议依赖标准和作战测试工具,但一些有价值的信息和操作更容易使用代码完成。 +## ![✔] 5.7. Create a ‘maintenance endpoint’ -**否则:** 您会发现,您正在执行许多“诊断部署” – 将代码发送到生产中,仅仅只为了诊断目的提取一些信息。 +**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tests tools, some valuable information and operations are easier done using code +**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes -🔗 [**更多: 创建一个 '维护端点'**](/sections/production/createmaintenanceendpoint.chinese.md) +🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md)

-## ![✔] 5.8. 使用APM产品发现错误和宕机时间 - -**TL;DR:** 监控和性能的产品(即APM)先前一步地评估代码库和API,自动的超过传统的监测,并测量在服务和层级上的整体用户体验。例如,一些APM产品可以突显导致最终用户负载过慢的事务,同时指出根本原因。 - -**否则:** 你可能会花大力气测量API性能和停机时间,也许你永远不会知道,真实场景下哪个是你最慢的代码部分,这些怎么影响用户体验。 +## ![✔] 5.8. Discover errors and downtime using APM products +**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause -🔗 [**更多: 使用APM产品发现错误和宕机时间**](/sections/production/apmproducts.chinese.md) +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affects the UX +🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md)

+## ![✔] 5.9. Make your code production-ready -## ![✔] 5.9. 使您的代码保持生产环境就绪 +**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) -**TL;DR:** 在意识中抱着最终上线的想法进行编码,从第1天开始计划上线。这听起来有点模糊,所以我编写了一些与生产维护密切相关的开发技巧(点击下面的要点) +**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written -**否则:** 一个世界冠军级别的IT/运维人员也不能拯救一个编码低劣的系统。 - - -🔗 [**更多: 使您的代码保持生产环境就绪**](/sections/production/productoncode.chinese.md) +🔗 [**Read More: Make your code production-ready**](/sections/production/productoncode.md)

-## ![✔] 5.10. 测量和保护内存使用 - -**TL;DR:** Node.js和内存有引起争论的联系:V8引擎对内存的使用有稍微的限制(1.4GB),在node的代码里面有内存泄漏的很多途径 – 因此监视node的进程内存是必须的。在小应用程序中,你可以使用shell命令周期性地测量内存,但在中等规模的应用程序中,考虑把内存监控建成一个健壮的监控系统。 +## ![✔] 5.10. Measure and guard the memory usage -**否则:** 您的内存可能一天泄漏一百兆,就像曾发生在沃尔玛的一样。 +**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leaks memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large app consider baking your memory watch into a robust monitoring system +**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**更多: 测量和保护内存使用**](/sections/production/measurememory.chinese.md) +🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md)

+## ![✔] 5.11. Get your frontend assets out of Node -## ![✔] 5.11. Node外管理您的前端资源 - -**TL;DR:** 使用专门的中间件(nginx,S3,CDN)服务前端内容,这是因为在处理大量静态文件的时候,由于node的单线程模型,它的性能很受影响。 - -**否则:** 您的单个node线程将忙于传输成百上千的html/图片/angular/react文件,而不是分配其所有的资源为了其擅长的任务 – 服务动态内容 +**TL;DR:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single threaded model +**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content -🔗 [**更多: Node外管理您的前端资源**](/sections/production/frontendout.chinese.md) +🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md)

+## ![✔] 5.12. Be stateless, kill your Servers almost every day -## ![✔] 5.12. 保持无状态,几乎每天都要停下服务器 +**TL;DR:** Store any type of data (e.g. users session, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior -**TL;DR:** 在外部数据存储上,存储任意类型数据(例如用户会话,缓存,上传文件)。考虑间隔地停掉您的服务器或者使用 ‘serverless’ 平台(例如 AWS Lambda),这是一个明确的强化无状态的行为。 - -**否则:** 某个服务器上的故障将导致应用程序宕机,而不仅仅是停用故障机器。此外,由于依赖特定服务器,伸缩弹性会变得更具挑战性。 - - -🔗 [**更多: 保持无状态,几乎每天都要停下服务器**](/sections/production/bestateless.chinese.md) +**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server +🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md)

+## ![✔] 5.13. Use tools that automatically detect vulnerabilities -## ![✔] 5.13. 使用自动检测漏洞的工具 - -**TL;DR:** 即使是最有信誉的依赖项,比如Express,会有使系统处于危险境地的已知漏洞(随着时间推移)。通过使用社区的或者商业工具,不时的检查漏洞和警告(本地或者Github上),这类问题很容易被抑制,有些问题甚至可以立即修补。 - -**否则:** 否则: 在没有专用工具的情况下,使代码清除漏洞,需要不断地跟踪有关新威胁的在线出版物。相当繁琐。 +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can get easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**Otherwise:** Otherwise: Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious -🔗 [**更多: 使用自动检测漏洞的工具**](/sections/production/detectvulnerabilities.chinese.md) +🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md)

+## ![✔] 5.14. Assign ‘TransactionId’ to each log statement -## ![✔] 5.14. 在每一个log语句中指明 ‘TransactionId’ +**TL;DR:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside -**TL;DR:** 在每一个请求的每一条log入口,指明同一个标识符,transaction-id: {某些值}。然后在检查日志中的错误时,很容易总结出前后发生的事情。不幸的是,由于Node异步的天性自然,这是不容易办到的,看下代码里面的例子 +**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue -**否则:** 在没有上下文的情况下查看生产错误日志,这会使问题变得更加困难和缓慢去解决。 - - -🔗 [**更多: 在每一个log语句中指明 ‘TransactionId’**](/sections/production/assigntransactionid.chinese.md) +🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md)

+## ![✔] 5.15. Set NODE_ENV=production -## ![✔] 5.15. 设置NODE_ENV=production +**TL;DR:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many NPM packages determining the current environment and optimize their code for production -**TL;DR:** 设置环境变量NODE_ENV为‘production’ 或者 ‘development’,这是一个是否激活上线优化的标志 - 很多NPM的包通过它来判断当前的环境,据此优化生产环境代码。 +**Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes the slower by a factor of three! -**否则:** 遗漏这个简单的属性可能大幅减弱性能。例如,在使用Express作为服务端渲染页面的时候,如果未设置NODE_ENV,性能将会减慢大概三分之一! +🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md) +

-🔗 [**更多: 设置NODE_ENV=production**](/sections/production/setnodeenv.chinese.md) +## ![✔] 5.16. Design automated, atomic and zero-downtime deployments +**TL;DR:** Researches show that teams who perform many deployments – lowers the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improves the deployment process. You should probably achieve that using Docker combined with CI tools as they became the industry standard for streamlined deployment + +**Otherwise:** Long deployments -> production down time & human-related error -> team unconfident and in making deployment -> less deployments and features

+## ![✔] 5.17. Use an LTS release of Node.js -## ![✔] 5.16. 设计自动化、原子化和零停机时间部署 +**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements -**TL;DR:** 研究表明,执行许多部署的团队降低了严重上线问题的可能性。不需要危险的手动步骤和服务停机时间的快速和自动化部署大大改善了部署过程。你应该达到使用Docker结合CI工具,使他们成为简化部署的行业标准。 +**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain -**否则:** 长时间部署 -> 线上宕机 & 和人相关的错误 -> 团队部署时不自信 -> 更少的部署和需求 +🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md)


-

⬆ 返回顶部

+

⬆ Return to top

# `Security Practices` ## Our contributors are working on this section. Would you like to join?


+ # `Performance Practices` ## Our contributors are working on this section. Would you like to join? - -

+


# Milestones -To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project. + +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project

+## Translations + +All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! + +### Completed translations + +* ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) + +### Translations in progress + +* ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) +* ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) +* ![KR](/assets/flags/KR.png) [Korean](https://github.com/i0natan/nodebestpractices/blob/korean-translation/README.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) +* ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) +* ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) +* ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) + +


+ # Contributors + ## `Yoni Goldberg` -Independent Node.js consultant who works with customers at USA, Europe and Israel on building large-scale scalable Node applications. Many of the best practices above were first published on his blog post at [http://www.goldbergyoni.com](http://www.goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com + +Independent Node.js consultant who works with customers in USA, Europe, and Israel on building large-scale scalable Node applications. Many of the best practices above were first published in his blog post at [http://www.goldbergyoni.com](http://www.goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com ## `Ido Richter` -👨‍💻 Software engineer, 🌐 web developer, 🤖 emojis enthusiast. + +👨‍💻 Software engineer, 🌐 web developer, 🤖 emojis enthusiast ## `Refael Ackermann` [@refack](https://github.com/refack) <refack@gmail.com> (he/him) -Node.js Core Collaborator, been noding since 0.4, and have noded in multiple production sites. Founded `node4good` home of [`lodash-contrib`](https://github.com/node4good/lodash-contrib), [`formage`](https://github.com/node4good/formage), and [`asynctrace`](https://github.com/node4good/asynctrace). -`refack` on freenode, Twitter, GitHub, GMail, and many other platforms. DMs are open, happy to help. -## `Bruno Scheufler` -💻 full-stack web developer and Node.js enthusiast. +Node.js Core Collaborator, been noding since 0.4, and have noded in multiple production sites. Founded `node4good` home of [`lodash-contrib`](https://github.com/node4good/lodash-contrib), [`formage`](https://github.com/node4good/formage), and [`asynctrace`](https://github.com/node4good/asynctrace). +`refack` on freenode, Twitter, GitHub, GMail, and many other platforms. DMs are open, happy to help +## `Bruno Scheufler` -

+💻 full-stack web developer and Node.js enthusiast + +## `Kyle Martin` [@js-kyle](https://github.com/js-kyle) +Full Stack Developer based in New Zealand, interested in architecting and building Node.js applications to perform at global scale. Keen contributor to open source software, including Node.js Core. + +


# Thank You Notes -This repository is being kept up to date thanks to the help from the community. We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A :sunflower: marks a successful pull request and a :star: marks an approved new best practice. - -🌻 [Kevin Rambaud](https://github.com/kevinrambaud), -🌻 [Michael Fine](https://github.com/mfine15), -🌻 [Shreya Dahal](https://github.com/squgeim), -🌻 [ChangJoo Park](https://github.com/ChangJoo-Park), -🌻 [Matheus Cruz Rocha](https://github.com/matheusrocha89), -🌻 [Yog Mehta](https://github.com/BitYog), -🌻 [Kudakwashe Paradzayi](https://github.com/kudapara), -🌻 [t1st3](https://github.com/t1st3), -🌻 [mulijordan1976](https://github.com/mulijordan1976), -🌻 [Matan Kushner](https://github.com/matchai), -🌻 [Fabio Hiroki](https://github.com/fabiothiroki), -🌻 [James Sumners](https://github.com/jsumners), -🌻 [Chandan Rai](https://github.com/crowchirp), -🌻 [Dan Gamble](https://github.com/dan-gamble), -🌻 [PJ Trainor](https://github.com/trainorpj), -🌻 [Remek Ambroziak](https://github.com/reod), -🌻 [Yoni Jah](https://github.com/yonjah), -🌻 [Misha Khokhlov](https://github.com/hazolsky), -🌻 [Evgeny Orekhov](https://github.com/EvgenyOrekhov), -🌻 [Gediminas Petrikas](https://github.com/gediminasml), -🌻 [Isaac Halvorson](https://github.com/hisaac), -🌻 [Vedran Karačić](https://github.com/vkaracic), -🌻 [lallenlowe](https://github.com/lallenlowe), -🌻 [Nathan Wells](https://github.com/nwwells), -🌻 [Paulo Vítor S Reis](https://github.com/paulovitin), +This repository is being kept up to date thanks to the help from the community. We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A 🌻 marks a successful pull request and a ⭐ marks an approved new best practice + +### Flowers
+ +🌻 [Kevin Rambaud](https://github.com/kevinrambaud), +🌻 [Michael Fine](https://github.com/mfine15), +🌻 [Shreya Dahal](https://github.com/squgeim), +🌻 [ChangJoo Park](https://github.com/ChangJoo-Park), +🌻 [Matheus Cruz Rocha](https://github.com/matheusrocha89), +🌻 [Yog Mehta](https://github.com/BitYog), +🌻 [Kudakwashe Paradzayi](https://github.com/kudapara), +🌻 [t1st3](https://github.com/t1st3), +🌻 [mulijordan1976](https://github.com/mulijordan1976), +🌻 [Matan Kushner](https://github.com/matchai), +🌻 [Fabio Hiroki](https://github.com/fabiothiroki), +🌻 [James Sumners](https://github.com/jsumners), +🌻 [Chandan Rai](https://github.com/crowchirp), +🌻 [Dan Gamble](https://github.com/dan-gamble), +🌻 [PJ Trainor](https://github.com/trainorpj), +🌻 [Remek Ambroziak](https://github.com/reod), +🌻 [Yoni Jah](https://github.com/yonjah), +🌻 [Misha Khokhlov](https://github.com/hazolsky), +🌻 [Evgeny Orekhov](https://github.com/EvgenyOrekhov), +🌻 [Gediminas Petrikas](https://github.com/gediminasml), +🌻 [Isaac Halvorson](https://github.com/hisaac), +🌻 [Vedran Karačić](https://github.com/vkaracic), +🌻 [lallenlowe](https://github.com/lallenlowe), +🌻 [Nathan Wells](https://github.com/nwwells), +🌻 [Paulo Vítor S Reis](https://github.com/paulovitin), 🌻 [syzer](https://github.com/syzer), 🌻 [David Sancho](https://github.com/davesnx), 🌻 [Robert Manolea](https://github.com/pupix), @@ -725,10 +729,18 @@ This repository is being kept up to date thanks to the help from the community. 🌻 [Leonardo Villela](https://github.com/leonardovillela), 🌻 [Michal Zalecki](https://github.com/MichalZalecki) 🌻 [Chris Nicola](https://github.com/chrisnicola), -🌻 [Alejandro Corredor](https://github.com/aecorredor) +🌻 [Alejandro Corredor](https://github.com/aecorredor), +🌻 [Ye Min Htut](https://github.com/ymhtut), +🌻 [cwar](https://github.com/cwar), +🌻 [Yuwei](https://github.com/keyfoxth), +🌻 [Utkarsh Bhatt](https://github.com/utkarshbhatt12) +🌻 [Duarte Mendes](https://github.com/duartemendes) +🌻 [Sagir Khan](https://github.com/sagirk) +🌻 [Jason Kim](https://github.com/serv) -
-:star: **No Stars Yet, Waiting For The First To Suggest a New Bullet** +### Stars
-

+⭐ [Kyle Martin](https://github.com/js-kyle) + +


From 4302dbfe202185629dcde4995c0bcfcf19810cc9 Mon Sep 17 00:00:00 2001 From: SangHyoun Yun Date: Sun, 10 Jun 2018 01:38:19 +0900 Subject: [PATCH 0002/1795] Translated important part of main page - Only translated title, and some contents of main page --- README.korean.md | 142 +++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/README.korean.md b/README.korean.md index 89a2354ec..4264b7df0 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1,6 +1,6 @@ [✔]: assets/images/checkbox-small-blue.png -# Node.js Best Practices +# Node.js 모범 사례

Node.js Best Practices @@ -14,37 +14,37 @@
-[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) +[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **트위터에서 팔로우 하세요!** [**@nodepractices**](https://twitter.com/nodepractices/)
-Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR**, ![RU](/assets/flags/RU.png)**RU** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) +다른 언어로 읽기: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR**, ![RU](/assets/flags/RU.png)**RU**, ![TR](/assets/flags/TR.png)**TR** 는 작업중입니다!)](#translations)
-# Welcome! 3 Things You Ought To Know First: +# 안녕하세요! 먼저 알아야 할 3가지가 있습니다: -**1. When you read here, you in fact read dozens of the best Node.js articles -** this is a summary and curation of the top-ranked content on Node.js best practices +**1. 이 문서를 읽는 것은, 사실상 수십 개의 베스트 Node.js 문서를 읽는 것입니다. -** 이 문서는 Node.js 의 가장 인기 있는 모범사례(Best Practice)들을 모은 요약집 및 큐레이션입니다. -**2. It is the largest compilation, and it is growing every week -** currently, more than 50 best practices, style guides, and architectural tips are presented. New issues and PR are created every day to keep this live book updated. We'd love to see you contributing here, whether fixing some code mistake or suggesting brilliant new ideas. See our [milestones here](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) +**2. 가장 큰 모음집이며, 매주 성장하고 있습니다. -** 현재, 50개 이상의 모범사례들과, 스타일 가이드, 아키텍처적인 팁들이 제공되고 있습니다. 이 문서의 업데이트를 위해 새로운 이슈들과 PR들이 매일 만들어지고 있습니다. 우리는 이 문서의 잘못된 코드를 고치거나 새로운 아이디어들을 제안하는 것을 매우 환영합니다. [마일스톤 보러가기](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) -**3. Most bullets have additional info -** nearby most best practice bullets you'll find **🔗Read More** link that will present you with code examples, quotes from selected blogs and more info +**3. 항목 대부분은 추가적인 정보가 있습니다 -** 항목 옆쪽에 존재하는 **🔗자세히 보기** 링크에서 코드 예제, 참조 블로그 또는 기타 정보들을 확인 할 수 있습니다.


-## Table of Contents +## 목차 -1. [Project structure Practices (5)](#1-project-structure-practices) -2. [Error Handling Practices (11) ](#2-error-handling-practices) -3. [Code Style Practices (12) ](#3-code-style-practices) -4. [Testing And Overall Quality Practices (8) ](#4-testing-and-overall-quality-practices) -5. [Going To Production Practices (17) ](#5-going-to-production-practices) -6. Security Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) -7. Performance Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) +1. [프로젝트 구조 설계 (5)](#1-프로젝트-구조-설계) +2. [에러 처리 방법 (11)](#2-에러-처리-방법) +3. [코드 스타일 (12) ](#3-코드-스타일) +4. [테스트 및 전체 품질 관리 (8) ](#4-테스트-및-전체-품질-관리) +5. [운영 환경으로 전환하기 (16) ](#5-운영-환경으로-전환하기) +6. 보안 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) +7. 성능 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open))


-# `1. Project Structure Practices` +# `1. 프로젝트 구조 설계` ## ![✔] 1.1 Structure your solution by components @@ -52,7 +52,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependant components - deployments become slower and more risky. It's also considered harder to scale-out when all the business units are not separated -🔗 [**Read More: structure by components**](/sections/projectstructre/breakintcomponents.md) +🔗 [**자세히 보가: structure by components**](/sections/projectstructre/breakintcomponents.korean.md)

@@ -62,7 +62,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** App that mixes web objects with other layers can not be accessed by testing code, CRON jobs and other non-Express callers -🔗 [**Read More: layer your app**](/sections/projectstructre/createlayers.md) +🔗 [**자세히 보가: layer your app**](/sections/projectstructre/createlayers.korean.md)

@@ -72,7 +72,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** You'll have to invent your own deployment and dependency wheel -🔗 [**Read More: Structure by feature**](/sections/projectstructre/wraputilities.md) +🔗 [**자세히 보가: Structure by feature**](/sections/projectstructre/wraputilities.korean.md)

@@ -82,7 +82,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file -🔗 [**Read More: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.md) +🔗 [**자세히 보가: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.korean.md)

@@ -92,13 +92,13 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both -🔗 [**Read More: configuration best practices**](/sections/projectstructre/configguide.md) +🔗 [**자세히 보가: configuration best practices**](/sections/projectstructre/configguide.korean.md)


-

⬆ Return to top

+

⬆ 목차로 돌아가기

-# `2. Error Handling Practices` +# `2. 에러 처리 방법` ## ![✔] 2.1 Use Async-Await or promises for async error handling @@ -106,7 +106,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting and awkward coding patterns -🔗 [**Read More: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.md) +🔗 [**자세히 보가: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.korean.md)

@@ -116,7 +116,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! -🔗 [**Read More: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.md) +🔗 [**자세히 보가: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.korean.md)

@@ -126,7 +126,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context -🔗 [**Read More: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.md) +🔗 [**자세히 보가: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.korean.md)

@@ -136,7 +136,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors -🔗 [**Read More: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.md) +🔗 [**자세히 보가: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.korean.md)

@@ -146,7 +146,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** An API client might decide to crash and restart only because he received back an error he couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) -🔗 [**Read More: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.md) +🔗 [**자세히 보가: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.korean.md)

@@ -156,7 +156,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** When an unfamiliar exception is caught, some object might be in a faulty state (e.g an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily -🔗 [**Read More: shutting the process**](/sections/errorhandling/shuttingtheprocess.md) +🔗 [**자세히 보가: shutting the process**](/sections/errorhandling/shuttingtheprocess.korean.md)

@@ -166,7 +166,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late -🔗 [**Read More: using a mature logger**](/sections/errorhandling/usematurelogger.md) +🔗 [**자세히 보가: using a mature logger**](/sections/errorhandling/usematurelogger.korean.md)

@@ -176,7 +176,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Without testing, whether automatically or manually, you can’t rely on our code to return the right errors. Without meaningful errors – there’s no error handling -🔗 [**Read More: testing error flows**](/sections/errorhandling/testingerrorflows.md) +🔗 [**자세히 보가: testing error flows**](/sections/errorhandling/testingerrorflows.korean.md)

@@ -186,7 +186,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX -🔗 [**Read More: using APM products**](/sections/errorhandling/apmproducts.md) +🔗 [**자세히 보가: using APM products**](/sections/errorhandling/apmproducts.korean.md)

@@ -196,7 +196,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about -🔗 [**Read More: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.md) +🔗 [**자세히 보가: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.korean.md)

@@ -206,13 +206,13 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines **Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? -🔗 [**Read More: failing fast**](/sections/errorhandling/failfast.md) +🔗 [**자세히 보가: failing fast**](/sections/errorhandling/failfast.korean.md)


-

⬆ Return to top

+

⬆ 목차로 돌아가기

-# `3. Code Style Practices` +# `3. 코드 스타일` ## ![✔] 3.1 Use ESLint @@ -251,7 +251,7 @@ function someFunction() **Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: -🔗 [**Read more:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) +🔗 [**자세히 보가:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

@@ -301,7 +301,7 @@ function doSomething() {} **Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes -🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) +🔗 [**자세히 보가: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75)

@@ -369,7 +369,7 @@ All statements above will return false if used with `===` **Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and make it difficult to reason about the code flow -🔗[**Read more:** Guide to async await 1.0](https://github.com/yortus/asyncawait) +🔗[**자세히 보가:** Guide to async await 1.0](https://github.com/yortus/asyncawait)

@@ -383,9 +383,9 @@ All statements above will return false if used with `===`


-

⬆ Return to top

+

⬆ 목차로 돌아가기

-# `4. Testing And Overall Quality Practices` +# `4. 테스트 및 전체 품질 관리` ## ![✔] 4.1 At the very least, write API (component) testing @@ -409,7 +409,7 @@ All statements above will return false if used with `===` **Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup -🔗 [**Read More: Choosing CI platform**](/sections/testingandquality/citools.md) +🔗 [**자세히 보가: Choosing CI platform**](/sections/testingandquality/citools.korean.md)

@@ -453,9 +453,9 @@ All statements above will return false if used with `===`


-

⬆ Return to top

+

⬆ 목차로 돌아가기

-# `5. Going To Production Practices` +# `5. 운영 환경으로 전환하기` ## ![✔] 5.1. Monitoring! @@ -463,7 +463,7 @@ All statements above will return false if used with `===` **Otherwise:** Failure === disappointed customers. Simple -🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md) +🔗 [**자세히 보가: Monitoring!**](/sections/production/monitoring.korean.md)

@@ -473,7 +473,7 @@ All statements above will return false if used with `===` **Otherwise:** You end-up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information -🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md) +🔗 [**자세히 보가: Increase transparency using smart logging**](/sections/production/smartlogging.korean.md)

@@ -483,7 +483,7 @@ All statements above will return false if used with `===` **Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly -🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md) +🔗 [**자세히 보가: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.korean.md)

@@ -493,7 +493,7 @@ All statements above will return false if used with `===` **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently at production. Even worse, different servers at the same production cluster might run different code -🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md) +🔗 [**자세히 보가: Lock dependencies**](/sections/production/lockdependencies.korean.md)

@@ -503,7 +503,7 @@ All statements above will return false if used with `===` **Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to a DevOps chaos -🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md) +🔗 [**자세히 보가: Guard process uptime using the right tool**](/sections/production/guardprocess.korean.md)

@@ -513,7 +513,7 @@ All statements above will return false if used with `===` **Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) -🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md) +🔗 [**자세히 보가: Utilize all CPU cores**](/sections/production/utilizecpu.korean.md)

@@ -523,7 +523,7 @@ All statements above will return false if used with `===` **Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes -🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md) +🔗 [**자세히 보가: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.korean.md)

@@ -533,7 +533,7 @@ All statements above will return false if used with `===` **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affects the UX -🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md) +🔗 [**자세히 보가: Discover errors and downtime using APM products**](/sections/production/apmproducts.korean.md)

@@ -543,7 +543,7 @@ All statements above will return false if used with `===` **Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written -🔗 [**Read More: Make your code production-ready**](/sections/production/productoncode.md) +🔗 [**자세히 보가: Make your code production-ready**](/sections/production/productoncode.korean.md)

@@ -553,7 +553,7 @@ All statements above will return false if used with `===` **Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md) +🔗 [**자세히 보가: Measure and guard the memory usage**](/sections/production/measurememory.korean.md)

@@ -563,7 +563,7 @@ All statements above will return false if used with `===` **Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content -🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md) +🔗 [**자세히 보가: Get your frontend assets out of Node**](/sections/production/frontendout.korean.md)

@@ -573,7 +573,7 @@ All statements above will return false if used with `===` **Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server -🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md) +🔗 [**자세히 보가: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.korean.md)

@@ -583,7 +583,7 @@ All statements above will return false if used with `===` **Otherwise:** Otherwise: Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious -🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md) +🔗 [**자세히 보가: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.korean.md)

@@ -593,7 +593,7 @@ All statements above will return false if used with `===` **Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue -🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md) +🔗 [**자세히 보가: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.korean.md)

@@ -603,7 +603,7 @@ All statements above will return false if used with `===` **Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes the slower by a factor of three! -🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md) +🔗 [**자세히 보가: Set NODE_ENV=production**](/sections/production/setnodeenv.korean.md)

@@ -621,39 +621,39 @@ All statements above will return false if used with `===` **Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain -🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md) +🔗 [**자세히 보가: Use an LTS release of Node.js**](/sections/production/LTSrelease.korean.md)


-

⬆ Return to top

+

⬆ 목차로 돌아가기

-# `Security Practices` +# `보안` -## Our contributors are working on this section. Would you like to join? +## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까?


-# `Performance Practices` +# `성능` -## Our contributors are working on this section. Would you like to join? +## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까?


-# Milestones +# 마일스톤 -To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project +이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. 만약 이 프로젝트에 기여를 하고 싶으시면 [마일스톤](https://github.com/i0natan/nodebestpractices/milestones) 을 보고 참여하십시오.

-## Translations +## 번역 -All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! +모든 번역은 커뮤니티에 의해 기여되고 있습니다. 이미 완성된 번역이나, 진행중, 새로운 번역에 대한 도움은 언제나 환영합니다! -### Completed translations +### 번역 작업 완료 * ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) -### Translations in progress +### 번역 작업중 * ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) * ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) From de75cfd0e192c62d94c7e2a2bc33dc8ead6c6171 Mon Sep 17 00:00:00 2001 From: SangHyoun Yun Date: Sun, 10 Jun 2018 02:04:59 +0900 Subject: [PATCH 0003/1795] Fix typo in 'READ MORE' --- README.korean.md | 72 ++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/README.korean.md b/README.korean.md index 4264b7df0..2afc8a1cb 100644 --- a/README.korean.md +++ b/README.korean.md @@ -52,7 +52,7 @@ **Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependant components - deployments become slower and more risky. It's also considered harder to scale-out when all the business units are not separated -🔗 [**자세히 보가: structure by components**](/sections/projectstructre/breakintcomponents.korean.md) +🔗 [**자세히 보기: structure by components**](/sections/projectstructre/breakintcomponents.korean.md)

@@ -62,7 +62,7 @@ **Otherwise:** App that mixes web objects with other layers can not be accessed by testing code, CRON jobs and other non-Express callers -🔗 [**자세히 보가: layer your app**](/sections/projectstructre/createlayers.korean.md) +🔗 [**자세히 보기: layer your app**](/sections/projectstructre/createlayers.korean.md)

@@ -72,7 +72,7 @@ **Otherwise:** You'll have to invent your own deployment and dependency wheel -🔗 [**자세히 보가: Structure by feature**](/sections/projectstructre/wraputilities.korean.md) +🔗 [**자세히 보기: Structure by feature**](/sections/projectstructre/wraputilities.korean.md)

@@ -82,7 +82,7 @@ **Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file -🔗 [**자세히 보가: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.korean.md) +🔗 [**자세히 보기: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.korean.md)

@@ -92,7 +92,7 @@ **Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both -🔗 [**자세히 보가: configuration best practices**](/sections/projectstructre/configguide.korean.md) +🔗 [**자세히 보기: configuration best practices**](/sections/projectstructre/configguide.korean.md)


@@ -106,7 +106,7 @@ **Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting and awkward coding patterns -🔗 [**자세히 보가: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.korean.md) +🔗 [**자세히 보기: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.korean.md)

@@ -116,7 +116,7 @@ **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! -🔗 [**자세히 보가: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.korean.md) +🔗 [**자세히 보기: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.korean.md)

@@ -126,7 +126,7 @@ **Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context -🔗 [**자세히 보가: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.korean.md) +🔗 [**자세히 보기: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.korean.md)

@@ -136,7 +136,7 @@ **Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors -🔗 [**자세히 보가: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.korean.md) +🔗 [**자세히 보기: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.korean.md)

@@ -146,7 +146,7 @@ **Otherwise:** An API client might decide to crash and restart only because he received back an error he couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) -🔗 [**자세히 보가: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.korean.md) +🔗 [**자세히 보기: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.korean.md)

@@ -156,7 +156,7 @@ **Otherwise:** When an unfamiliar exception is caught, some object might be in a faulty state (e.g an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily -🔗 [**자세히 보가: shutting the process**](/sections/errorhandling/shuttingtheprocess.korean.md) +🔗 [**자세히 보기: shutting the process**](/sections/errorhandling/shuttingtheprocess.korean.md)

@@ -166,7 +166,7 @@ **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late -🔗 [**자세히 보가: using a mature logger**](/sections/errorhandling/usematurelogger.korean.md) +🔗 [**자세히 보기: using a mature logger**](/sections/errorhandling/usematurelogger.korean.md)

@@ -176,7 +176,7 @@ **Otherwise:** Without testing, whether automatically or manually, you can’t rely on our code to return the right errors. Without meaningful errors – there’s no error handling -🔗 [**자세히 보가: testing error flows**](/sections/errorhandling/testingerrorflows.korean.md) +🔗 [**자세히 보기: testing error flows**](/sections/errorhandling/testingerrorflows.korean.md)

@@ -186,7 +186,7 @@ **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX -🔗 [**자세히 보가: using APM products**](/sections/errorhandling/apmproducts.korean.md) +🔗 [**자세히 보기: using APM products**](/sections/errorhandling/apmproducts.korean.md)

@@ -196,7 +196,7 @@ **Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about -🔗 [**자세히 보가: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.korean.md) +🔗 [**자세히 보기: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.korean.md)

@@ -206,7 +206,7 @@ **Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? -🔗 [**자세히 보가: failing fast**](/sections/errorhandling/failfast.korean.md) +🔗 [**자세히 보기: failing fast**](/sections/errorhandling/failfast.korean.md)


@@ -251,7 +251,7 @@ function someFunction() **Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: -🔗 [**자세히 보가:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) +🔗 [**자세히 보기:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

@@ -301,7 +301,7 @@ function doSomething() {} **Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes -🔗 [**자세히 보가: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) +🔗 [**자세히 보기: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75)

@@ -369,7 +369,7 @@ All statements above will return false if used with `===` **Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and make it difficult to reason about the code flow -🔗[**자세히 보가:** Guide to async await 1.0](https://github.com/yortus/asyncawait) +🔗[**자세히 보기:** Guide to async await 1.0](https://github.com/yortus/asyncawait)

@@ -409,7 +409,7 @@ All statements above will return false if used with `===` **Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup -🔗 [**자세히 보가: Choosing CI platform**](/sections/testingandquality/citools.korean.md) +🔗 [**자세히 보기: Choosing CI platform**](/sections/testingandquality/citools.korean.md)

@@ -463,7 +463,7 @@ All statements above will return false if used with `===` **Otherwise:** Failure === disappointed customers. Simple -🔗 [**자세히 보가: Monitoring!**](/sections/production/monitoring.korean.md) +🔗 [**자세히 보기: Monitoring!**](/sections/production/monitoring.korean.md)

@@ -473,7 +473,7 @@ All statements above will return false if used with `===` **Otherwise:** You end-up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information -🔗 [**자세히 보가: Increase transparency using smart logging**](/sections/production/smartlogging.korean.md) +🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.korean.md)

@@ -483,7 +483,7 @@ All statements above will return false if used with `===` **Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly -🔗 [**자세히 보가: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.korean.md) +🔗 [**자세히 보기: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.korean.md)

@@ -493,7 +493,7 @@ All statements above will return false if used with `===` **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently at production. Even worse, different servers at the same production cluster might run different code -🔗 [**자세히 보가: Lock dependencies**](/sections/production/lockdependencies.korean.md) +🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.korean.md)

@@ -503,7 +503,7 @@ All statements above will return false if used with `===` **Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to a DevOps chaos -🔗 [**자세히 보가: Guard process uptime using the right tool**](/sections/production/guardprocess.korean.md) +🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.korean.md)

@@ -513,7 +513,7 @@ All statements above will return false if used with `===` **Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) -🔗 [**자세히 보가: Utilize all CPU cores**](/sections/production/utilizecpu.korean.md) +🔗 [**자세히 보기: Utilize all CPU cores**](/sections/production/utilizecpu.korean.md)

@@ -523,7 +523,7 @@ All statements above will return false if used with `===` **Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes -🔗 [**자세히 보가: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.korean.md) +🔗 [**자세히 보기: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.korean.md)

@@ -533,7 +533,7 @@ All statements above will return false if used with `===` **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affects the UX -🔗 [**자세히 보가: Discover errors and downtime using APM products**](/sections/production/apmproducts.korean.md) +🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.korean.md)

@@ -543,7 +543,7 @@ All statements above will return false if used with `===` **Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written -🔗 [**자세히 보가: Make your code production-ready**](/sections/production/productoncode.korean.md) +🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productoncode.korean.md)

@@ -553,7 +553,7 @@ All statements above will return false if used with `===` **Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**자세히 보가: Measure and guard the memory usage**](/sections/production/measurememory.korean.md) +🔗 [**자세히 보기: Measure and guard the memory usage**](/sections/production/measurememory.korean.md)

@@ -563,7 +563,7 @@ All statements above will return false if used with `===` **Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content -🔗 [**자세히 보가: Get your frontend assets out of Node**](/sections/production/frontendout.korean.md) +🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.korean.md)

@@ -573,7 +573,7 @@ All statements above will return false if used with `===` **Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server -🔗 [**자세히 보가: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.korean.md) +🔗 [**자세히 보기: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.korean.md)

@@ -583,7 +583,7 @@ All statements above will return false if used with `===` **Otherwise:** Otherwise: Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious -🔗 [**자세히 보가: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.korean.md) +🔗 [**자세히 보기: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.korean.md)

@@ -593,7 +593,7 @@ All statements above will return false if used with `===` **Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue -🔗 [**자세히 보가: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.korean.md) +🔗 [**자세히 보기: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.korean.md)

@@ -603,7 +603,7 @@ All statements above will return false if used with `===` **Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes the slower by a factor of three! -🔗 [**자세히 보가: Set NODE_ENV=production**](/sections/production/setnodeenv.korean.md) +🔗 [**자세히 보기: Set NODE_ENV=production**](/sections/production/setnodeenv.korean.md)

@@ -621,7 +621,7 @@ All statements above will return false if used with `===` **Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain -🔗 [**자세히 보가: Use an LTS release of Node.js**](/sections/production/LTSrelease.korean.md) +🔗 [**자세히 보기: Use an LTS release of Node.js**](/sections/production/LTSrelease.korean.md)


From 1e46ddcb8da5a5ea7f8e4f8b7617d497bd5d1461 Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Fri, 17 Aug 2018 01:54:23 +0900 Subject: [PATCH 0004/1795] Translate 1.1 and 1.2 to Korean --- README.korean.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.korean.md b/README.korean.md index 2afc8a1cb..9d15b644a 100644 --- a/README.korean.md +++ b/README.korean.md @@ -46,23 +46,23 @@ # `1. 프로젝트 구조 설계` -## ![✔] 1.1 Structure your solution by components +## ![✔] 1.1 컴포넌트 기반으로 설계하라 -**TL;DR:** The worst large applications pitfall is maintaining a huge code base with hundreds of dependencies - such a monolith slows down developers as they try to incorporate new features. Instead, partition your code into components, each gets its own folder or a dedicated codebase, and ensure that each unit is kept small and simple. Visit 'Read More' below to see examples of correct project structure +**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 가장 안좋은 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바를 프로젝트 구조의 예시를 확인하라. -**Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependant components - deployments become slower and more risky. It's also considered harder to scale-out when all the business units are not separated +**그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 미치는 영향을 깨닫기위해 몸부림치거나 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 할때 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다. -🔗 [**자세히 보기: structure by components**](/sections/projectstructre/breakintcomponents.korean.md) +🔗 [**자세히 보기: 컴포넌트로 구조화하기**](/sections/projectstructre/breakintcomponents.korean.md)

-## ![✔] 1.2 Layer your components, keep Express within its boundaries +## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 두세요. -**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (Express req, res) to business logic and data layers - this makes your application dependant on and accessible by Express only +**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 가짜 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것은 굉장히 일반적인 패턴이지만, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 이것은 당신의 어플리케이션에 의존성을 만들고 Expres에서만 접근 가능하도록 만든다. -**Otherwise:** App that mixes web objects with other layers can not be accessed by testing code, CRON jobs and other non-Express callers +**그렇게 하지 않을 경우:** 웹 객체를 다른 계층과 뒤섞은 앱은 테스트 코드, CRON 작업이나 Express가 아닌 다른 곳에서 접근이 불가능하게 한다. -🔗 [**자세히 보기: layer your app**](/sections/projectstructre/createlayers.korean.md) +🔗 [**자세히 보기: 앱을 계층화하기**](/sections/projectstructre/createlayers.korean.md)

From 1b3292560848052945bdc7fd4c680ddb026151f8 Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Fri, 17 Aug 2018 09:47:17 +0900 Subject: [PATCH 0005/1795] Fix 1.2 title --- README.korean.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.korean.md b/README.korean.md index 9d15b644a..9363cdb36 100644 --- a/README.korean.md +++ b/README.korean.md @@ -56,7 +56,7 @@

-## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 두세요. +## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라. **핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 가짜 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것은 굉장히 일반적인 패턴이지만, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 이것은 당신의 어플리케이션에 의존성을 만들고 Expres에서만 접근 가능하도록 만든다. From 000ad87f895265378f16596732bf01a03e2b952e Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Fri, 17 Aug 2018 22:27:20 +0900 Subject: [PATCH 0006/1795] Translate 1.3 and 1.4 to Korean --- README.korean.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.korean.md b/README.korean.md index 9363cdb36..7c72dfe53 100644 --- a/README.korean.md +++ b/README.korean.md @@ -66,23 +66,23 @@

-## ![✔] 1.3 Wrap common utilities as NPM packages +## ![✔] 1.3 유틸리티들을 NPM 패키지로 감싸라(wrap) -**TL;DR:** In a large app that constitutes a large code base, cross-cutting-concern utilities like logger, encryption and alike, should be wrapped by your own code and exposed as private NPM packages. This allows sharing them among multiple code bases and projects +**핵심요약:** 커다란 코드 기반으로 구성되어있는 커다란 앱에서는 로깅, 암호화 같은 횡단 관심사(cross-cutting-concern)가 존재하는 유틸의 경우 당신 자신의 코드로 감싸져야하며 개인 NPM package로 노출이 되어야한다. 이것은 여러 코드 기반과 프로젝트들 사이에서 그것들을 공유가 가능하도록 해준다. -**Otherwise:** You'll have to invent your own deployment and dependency wheel +**그렇게 하지 않을 경우:** 당신 자신만의 배포 및 의존성 바퀴(wheel)를 새로 발명해야 할 것이다. -🔗 [**자세히 보기: Structure by feature**](/sections/projectstructre/wraputilities.korean.md) +🔗 [**자세히 보기: 기능으로 구조화 하기**](/sections/projectstructre/wraputilities.korean.md)

-## ![✔] 1.4 Separate Express 'app' and 'server' +## ![✔] 1.4 Express의 app과 server를 분리하라 -**TL;DR:** Avoid the nasty habit of defining the entire [Express](https://expressjs.com/) app in a single huge file - separate your 'Express' definition to at least two files: the API declaration (app.js) and the networking concerns (WWW). For even better structure, locate your API declaration within components +**핵심요약:** 'Express' 정의를 적어도 API 선언(app.js)과 네트워크 부분(WWW)의 두 개 파일로 나눠서 전체 [Express](https://expressjs.com/)앱을 하나의 큰 파일에 정의하는 불쾌한 습관을 피해라. 더 좋은 구조는 API 선언을 컴포넌트에 위치시키는 것이다. -**Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file +**그렇게 하지 않을 경우:** API는 HTTP 요청으로만 테스트가 가능 할것이다(커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것은 큰 기쁨이 아닐 것이다. -🔗 [**자세히 보기: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.korean.md) +🔗 [**자세히 보기: Express를 'app'과 'server'로 분리하기**](/sections/projectstructre/separateexpress.korean.md)

From 6410475b113bdd4c883908ecec9c11498743120b Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Fri, 17 Aug 2018 22:45:33 +0900 Subject: [PATCH 0007/1795] Fix typo --- README.korean.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.korean.md b/README.korean.md index 7c72dfe53..f03480a58 100644 --- a/README.korean.md +++ b/README.korean.md @@ -58,7 +58,7 @@ ## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라. -**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 가짜 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것은 굉장히 일반적인 패턴이지만, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 이것은 당신의 어플리케이션에 의존성을 만들고 Expres에서만 접근 가능하도록 만든다. +**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 가짜 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것은 굉장히 일반적인 패턴이지만, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 이것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다. **그렇게 하지 않을 경우:** 웹 객체를 다른 계층과 뒤섞은 앱은 테스트 코드, CRON 작업이나 Express가 아닌 다른 곳에서 접근이 불가능하게 한다. From e68425960a19348c57154db7e01aaa49c4563179 Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Sat, 18 Aug 2018 00:54:49 +0900 Subject: [PATCH 0008/1795] Translate 1.5 and 2.1 to Korean --- README.korean.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.korean.md b/README.korean.md index f03480a58..f9ab94e4f 100644 --- a/README.korean.md +++ b/README.korean.md @@ -86,13 +86,13 @@

-## ![✔] 1.5 Use environment aware, secure and hierarchical config +## ![✔] 1.5 환경을 인식하는, 보안적인, 계층적인 설정을 사용하라 -**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) +**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경 변수에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 바깥에서 관리되어야하고 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. -**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both +**그렇게 하지 않을 경우:** 위의 구성 요구사항 중 어느 것도 만족시키지 못한다면 개발팀 혹은 데브옵스팀을 늪으로 몰아갈 수 있다. 아마도 두 팀 모두일 것이다. -🔗 [**자세히 보기: configuration best practices**](/sections/projectstructre/configguide.korean.md) +🔗 [**자세히 보기: 구성 모범 사례**](/sections/projectstructre/configguide.korean.md)


@@ -100,13 +100,13 @@ # `2. 에러 처리 방법` -## ![✔] 2.1 Use Async-Await or promises for async error handling +## ![✔] 2.1 비동기 에러 처리시에는 Async-Await 혹은 Promise를 사용하라 -**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using a reputable promise library or async-await instead which enables a much more compact and familiar code syntax like try-catch +**핵심요약:** 비동기 에러를 콜백 스타일로 처리하는 것은 지옥으로 가는 급행열차일 것이다(운명의 피라미드로 잘 알려진). 당신이 코드에 줄 수 있는 가장 큰 선물은 평판이 좋은 Promise 라이브러리를 사용하거나 훨신 작고 친숙한 코드 문법인 try-catch를 사용하게 해주는 Async-Await를 사용하는 것이다. -**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting and awkward coding patterns +**그렇게 하지 않을 경우:** Node.js 콜백 스타일인 function(err, response)는 에러 처리와 일반 코드의 혼합, 코드의 과도한 중첩, 이상한 코딩 패턴 때문에 유지보수가 불가능한 코드로가는 확실한 길이다. -🔗 [**자세히 보기: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.korean.md) +🔗 [**자세히 보기: 콜백 피하기**](/sections/errorhandling/asyncerrorhandling.korean.md)

From 76f92d2a003a93ece08a4d1d7f2c23ac86d618c1 Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Sat, 18 Aug 2018 10:56:57 +0900 Subject: [PATCH 0009/1795] Translate 2.2 and 2.3 to Korean --- README.korean.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.korean.md b/README.korean.md index f9ab94e4f..c436b84c2 100644 --- a/README.korean.md +++ b/README.korean.md @@ -110,23 +110,23 @@

-## ![✔] 2.2 Use only the built-in Error object +## ![✔] 2.2 내장된 Error 객체만 사용하라 -**TL;DR:** Many throws errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Whether you reject a promise, throw an exception or an emit error – using only the built-in Error object will increase uniformity and prevent loss of information +**핵심요약:** 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던진다(throw). 이것은 에러처리 로직과 모듈 사이의 상호운영성을 복잡하게 한다. 당신이 Promise를 거부(reject)하든, 예외를 던지든, 에러를 냈건 내장된 Error 객체를 이용하는 것은 균일성을 향상하고 정보의 손실을 방지하게 만들것이다. -**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! +**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워 질것이다. 더 나쁜 것은, 사용자가 정의한 타입으로 에러를 나타내는 것은 스택 트래이스와 같은 중요한 에러 정보를 손실할 가능성이 있다는 것이다! -🔗 [**자세히 보기: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.korean.md) +🔗 [**자세히 보기: 내장된 Error 객체 사용하기**](/sections/errorhandling/useonlythebuiltinerror.korean.md)

-## ![✔] 2.3 Distinguish operational vs programmer errors +## ![✔] 2.3 동작상의 에러와 프로그래머 에러를 구분하라 -**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, programmer error (e.g. trying to read undefined variable) refers to unknown code failures that dictate to gracefully restart the application +**핵심요약:** API에서 잘못된 입력을 받는 것과 같은 동작상의 에러는 에러의 영향을 완전히 이해할수 있고 신중하게 처리 할수있는 알려진 경우를 의미한다. 반면에 정의되지 않은 변수를 읽는 것과 같은 프로그래머 에러는 어플리케이션을 우아하게 다시 시작하도록 만드는 알수 없는 코드 에러를 의미한다. -**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context +**그렇게 하지 않을 경우:** 당신은 에러가 날때마다 어플리케이션을 다시 시작할수도 있다. 하지만 왜 사소하고 예측가능한 동작상의 오류때문에 5000명의 온라인 사용자를 다운시키는 것인가? 나머지 상황 또한 이상적이지 않다. 알수없는 이슈(프로그래머 에러)가 났는데 어플리케이션을 그대로 두는 것은 예측이 불가능한 동작을 일으킬 수 있다. 두 가지를 구별하는 것은 현명한 행동과 주어진 상황에 따른 균형잡힌 접근을 가능하게 한다. -🔗 [**자세히 보기: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.korean.md) +🔗 [**자세히 보기: 동작상의 에러와 프로그래머 에러**](/sections/errorhandling/operationalvsprogrammererror.korean.md)

From 46fffce8a6c728ad7b1df360a9c339021555788b Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Sat, 18 Aug 2018 11:17:51 +0900 Subject: [PATCH 0010/1795] Fix typo and nuance of translations --- README.korean.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.korean.md b/README.korean.md index c436b84c2..85eafcece 100644 --- a/README.korean.md +++ b/README.korean.md @@ -48,7 +48,7 @@ ## ![✔] 1.1 컴포넌트 기반으로 설계하라 -**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 가장 안좋은 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바를 프로젝트 구조의 예시를 확인하라. +**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 가장 안좋은 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인하라. **그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 미치는 영향을 깨닫기위해 몸부림치거나 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 할때 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다. @@ -58,7 +58,7 @@ ## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라. -**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 가짜 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것은 굉장히 일반적인 패턴이지만, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 이것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다. +**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 모의 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것이 굉장히 일반적인 패턴임에도, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 그렇게 하는것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다. **그렇게 하지 않을 경우:** 웹 객체를 다른 계층과 뒤섞은 앱은 테스트 코드, CRON 작업이나 Express가 아닌 다른 곳에서 접근이 불가능하게 한다. @@ -80,7 +80,7 @@ **핵심요약:** 'Express' 정의를 적어도 API 선언(app.js)과 네트워크 부분(WWW)의 두 개 파일로 나눠서 전체 [Express](https://expressjs.com/)앱을 하나의 큰 파일에 정의하는 불쾌한 습관을 피해라. 더 좋은 구조는 API 선언을 컴포넌트에 위치시키는 것이다. -**그렇게 하지 않을 경우:** API는 HTTP 요청으로만 테스트가 가능 할것이다(커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것은 큰 기쁨이 아닐 것이다. +**그렇게 하지 않을 경우:** API는 HTTP 요청으로만 테스트가 가능 할것이다(커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것이 크게 즐겁지는 않을 것이다. 🔗 [**자세히 보기: Express를 'app'과 'server'로 분리하기**](/sections/projectstructre/separateexpress.korean.md) From 4f7cf4d66edd829c7ba12ebf771af726b75156bd Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Sun, 19 Aug 2018 17:32:05 +0900 Subject: [PATCH 0011/1795] Translate 2.4, 2.5 and 2.6 to Korean --- README.korean.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.korean.md b/README.korean.md index 85eafcece..250afa06b 100644 --- a/README.korean.md +++ b/README.korean.md @@ -130,33 +130,33 @@

-## ![✔] 2.4 Handle errors centrally, not within an Express middleware +## ![✔] 2.4 에러를 Express 미들웨어로 처리하지 말고 중앙집중적으로 처리하라 -**TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in +**핵심요약:** 관리자에게 메일을 보내거나 로깅을 하는 것과 같은 에러 처리는 에러가 발생할 때 모든 엔드포인트(예를 들어 Express 미들웨어, cron 작업, 단위 테스트 등)가 호출하는 에러전용 중앙집중 객체로 캡슐화 되어야한다. -**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors +**그렇게 하지 않을 경우:** 한 곳에서 에러를 처리하지 않는 것은 코드 중복과 부적절한 에러처리로 이어진다. -🔗 [**자세히 보기: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.korean.md) +🔗 [**자세히 보기: 중앙집중적으로 에러 처리하기**](/sections/errorhandling/centralizedhandling.korean.md)

-## ![✔] 2.5 Document API errors using Swagger +## ![✔] 2.5 Swagger를 이용해 API 에러를 문서화하라 -**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. This is usually done with REST API documentation frameworks like Swagger +**핵심요약:** API를 호출자들이 어떤 에러가 반환 될수 있는지 알게하여 충돌없이 신중하게 처리 할 수 있도록하라. 이것은 보통 Swagger와 같은 API 문서화 프레임워크를 통해 이루어진다. -**Otherwise:** An API client might decide to crash and restart only because he received back an error he couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) +**그렇게 하지 않을 경우:** API 클라이언트는 알수 없는 에러로 인해 충돌 후에 재시작을 결정할수도 있을 것이다. 참고: 당신의 API를 호출한 사람이 당신 자신일 수 있습니다(마이크로서비스 환경에서는 아주 일반적임). -🔗 [**자세히 보기: documenting errors in Swagger**](/sections/errorhandling/documentingusingswagger.korean.md) +🔗 [**자세히 보기: Swagger에서 에러 문서화하기**](/sections/errorhandling/documentingusingswagger.korean.md)

-## ![✔] 2.6 Shut the process gracefully when a stranger comes to town +## ![✔] 2.6 이상한 것이 들어왔을때 프로세스를 정상적으로 중단하라 -**TL;DR:** When an unknown error occurs (a developer error, see best practice number #3)- there is uncertainty about the application healthiness. A common practice suggests restarting the process carefully using a ‘restarter’ tool like Forever and PM2 +**핵심요약:** 알수 없는 에러(개발자 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. -**Otherwise:** When an unfamiliar exception is caught, some object might be in a faulty state (e.g an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily +**그렇게 하지 않을 경우:** 익숙치 않은 예외가 잡히면 일부 객체가 오류 상태(예를 들어 전역적으로 사용되고 내부 오류로 인해 이벤트를 더이상 발생시키지 않는 Event Emitter)일 수 있으며 모든 향후 요청이 실패하거나 비정상적(crazily)으로 동작할 수 있습니다. -🔗 [**자세히 보기: shutting the process**](/sections/errorhandling/shuttingtheprocess.korean.md) +🔗 [**자세히 보기: 프로세스 중단하기**](/sections/errorhandling/shuttingtheprocess.korean.md)

From e3f35fea3f06cfb8b0eb370491cb45d41892aa23 Mon Sep 17 00:00:00 2001 From: hansangbeom Date: Sun, 19 Aug 2018 17:51:27 +0900 Subject: [PATCH 0012/1795] Fix nuance of translations --- README.korean.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.korean.md b/README.korean.md index 250afa06b..7a72b9a4d 100644 --- a/README.korean.md +++ b/README.korean.md @@ -48,7 +48,7 @@ ## ![✔] 1.1 컴포넌트 기반으로 설계하라 -**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 가장 안좋은 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인하라. +**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 최악의 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인하라. **그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 미치는 영향을 깨닫기위해 몸부림치거나 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 할때 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다. @@ -114,7 +114,7 @@ **핵심요약:** 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던진다(throw). 이것은 에러처리 로직과 모듈 사이의 상호운영성을 복잡하게 한다. 당신이 Promise를 거부(reject)하든, 예외를 던지든, 에러를 냈건 내장된 Error 객체를 이용하는 것은 균일성을 향상하고 정보의 손실을 방지하게 만들것이다. -**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워 질것이다. 더 나쁜 것은, 사용자가 정의한 타입으로 에러를 나타내는 것은 스택 트래이스와 같은 중요한 에러 정보를 손실할 가능성이 있다는 것이다! +**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워 질것이다. 더 나쁜 것은, 사용자가 정의한 타입으로 에러를 나타내는 것은 스택 정보(stack trace)와 같은 중요한 에러 정보를 손실할 가능성이 있다는 것이다! 🔗 [**자세히 보기: 내장된 Error 객체 사용하기**](/sections/errorhandling/useonlythebuiltinerror.korean.md) @@ -142,9 +142,9 @@ ## ![✔] 2.5 Swagger를 이용해 API 에러를 문서화하라 -**핵심요약:** API를 호출자들이 어떤 에러가 반환 될수 있는지 알게하여 충돌없이 신중하게 처리 할 수 있도록하라. 이것은 보통 Swagger와 같은 API 문서화 프레임워크를 통해 이루어진다. +**핵심요약:** API를 호출한 사람들이 어떤 에러가 반환 될수 있는지 알게하여 충돌없이 신중하게 처리 할 수 있도록하라. 이것은 보통 Swagger와 같은 API 문서화 프레임워크를 통해 이루어진다. -**그렇게 하지 않을 경우:** API 클라이언트는 알수 없는 에러로 인해 충돌 후에 재시작을 결정할수도 있을 것이다. 참고: 당신의 API를 호출한 사람이 당신 자신일 수 있습니다(마이크로서비스 환경에서는 아주 일반적임). +**그렇게 하지 않을 경우:** API 클라이언트는 알수 없는 에러로 인해 충돌 후에 재시작을 결정할수도 있을 것이다. 참고: 당신의 API를 호출한 사람이 당신 자신 일수도 있다.(마이크로서비스 환경에서는 아주 일반적임). 🔗 [**자세히 보기: Swagger에서 에러 문서화하기**](/sections/errorhandling/documentingusingswagger.korean.md) @@ -152,9 +152,9 @@ ## ![✔] 2.6 이상한 것이 들어왔을때 프로세스를 정상적으로 중단하라 -**핵심요약:** 알수 없는 에러(개발자 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. +**핵심요약:** 알수 없는 에러(프로그래머 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. -**그렇게 하지 않을 경우:** 익숙치 않은 예외가 잡히면 일부 객체가 오류 상태(예를 들어 전역적으로 사용되고 내부 오류로 인해 이벤트를 더이상 발생시키지 않는 Event Emitter)일 수 있으며 모든 향후 요청이 실패하거나 비정상적(crazily)으로 동작할 수 있습니다. +**그렇게 하지 않을 경우:** 익숙치 않은 예외가 발생하면 일부 객체가 오류 상태(예를 들어 전역적으로 사용되지만 내부 오류로 인해 이벤트를 더이상 발생시키지 않는 Event Emitter)일 수 있으며 향후의 모든 요청이 실패하거나 미친것처럼(crazily) 동작할 수 있다. 🔗 [**자세히 보기: 프로세스 중단하기**](/sections/errorhandling/shuttingtheprocess.korean.md) From 4edc6917fff8f878e7686dbcebbe798b12d1ea6a Mon Sep 17 00:00:00 2001 From: Sangbeom Han Date: Mon, 3 Jun 2019 22:32:29 +0900 Subject: [PATCH 0013/1795] Translate section 4 to Korean --- README.korean.md | 611 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 511 insertions(+), 100 deletions(-) diff --git a/README.korean.md b/README.korean.md index 72bd840f8..ca36282e3 100644 --- a/README.korean.md +++ b/README.korean.md @@ -9,7 +9,7 @@
- 82 items Last update: April 13, 2019 Updated for Node 10.15.3 LTS + 83 items Last update: May 13, 2019 Updated for Node 12.0.0 LTS

@@ -22,6 +22,18 @@
+###### Built and maintained by our [Steering Committee](#steering-committee) and [Collaborators](#collaborators) + +# Latest Best Practices and News + +- **New best practice:** 4.4: [Avoid test-fixtures, add data per test](https://github.com/i0natan/nodebestpractices#4-testing-and-overall-quality-practices) + +- **New best practice:** 6.25: [Avoid publishing secrets to the npm registry](/sections/security/avoid_publishing_secrets.md) + +- **New translation:** ![BR](/assets/flags/BR.png) [Brazilian Portuguese](/README.brazilian-portuguese.md) available now, courtesy of [Marcelo Melo](https://github.com/marcelosdm)! ❤️ + +
+ # 안녕하세요! 먼저 알아야 할 3가지가 있습니다: **1. 이 문서를 읽는 것은, 사실상 수십 개의 베스트 Node.js 문서를 읽는 것입니다. -** 이 문서는 Node.js 의 가장 인기 있는 모범사례(Best Practice)들을 모은 요약집 및 큐레이션입니다. @@ -30,7 +42,7 @@ **3. 항목 대부분은 추가적인 정보가 있습니다 -** 항목 옆쪽에 존재하는 **🔗자세히 보기** 링크에서 코드 예제, 참조 블로그 또는 기타 정보들을 확인 할 수 있습니다. -


+

## 목차 @@ -42,7 +54,7 @@ 6. 보안 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) 7. 성능 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) -


+

# `1. 프로젝트 구조 설계` @@ -152,7 +164,7 @@ ## ![✔] 2.6 이상한 것이 들어왔을때 프로세스를 정상적으로 중단하라 -**핵심요약:** 알수 없는 에러(프로그래머 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. +**핵심요약:** 알수 없는 에러(프로그래머 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. **그렇게 하지 않을 경우:** 익숙치 않은 예외가 발생하면 일부 객체가 오류 상태(예를 들어 전역적으로 사용되지만 내부 오류로 인해 이벤트를 더이상 발생시키지 않는 Event Emitter)일 수 있으며 향후의 모든 요청이 실패하거나 미친것처럼(crazily) 동작할 수 있다. @@ -216,7 +228,7 @@ ## ![✔] 3.1 ESLint를 사용하라 -**핵심요약:** [ESLint](https://eslint.org)는 발생 가능한 코드 에러를 체크하고 껄끄러운 간격(spacing)문제를 식별하는 것부터 프로그래머가 분별없이 에러를 던지는 것과 같은 코드의 심각한 안티 패턴을 감지하여 코드 스타일을 바꾸는 것에 대한 사실상의 표준이다. ESLint도 자동으로 코드스타일을 고칠 수 있지만 [prettier](https://www.npmjs.com/package/prettier)와 [beautify](https://www.npmjs.com/package/js-beautify)같은 수정 부분의 포맷을 맞춰주는 강력한 툴이 있고 ESLint와 함께 작동된다. +**핵심요약:** [ESLint](https://eslint.org)는 발생 가능한 코드 에러를 체크하고 껄끄러운 간격(spacing)문제를 식별하는 것부터 프로그래머가 분별없이 에러를 던지는 것과 같은 코드의 심각한 안티 패턴을 감지하여 코드 스타일을 바꾸는 것에 대한 사실상의 표준이다. ESLint도 자동으로 코드스타일을 고칠 수 있지만 [prettier](https://www.npmjs.com/package/prettier)와 [beautify](https://www.npmjs.com/package/js-beautify)같은 수정 부분의 포맷을 맞춰주는 강력한 툴이 있고 ESLint와 함께 작동된다. **그렇게 하지 않을 경우:** 프로그래머가 쓸데없는 간격과 한줄의 길이(line-width) 문제에 대해서 집중해야하고 프로젝트의 코드스타일에 대해 과도하게 생각하느라 시간을 낭비해야할 수도 있다. @@ -267,12 +279,12 @@ function someFunction() **핵심요약:** 클로저와 콜백을 포함한 모든 함수에 이름을 붙여라. 익명함수를 피해라. 이것은 노드 앱을 프로파일링 할때 특히 유용하다. 모든 함수를 명명하는 것은 당신이 메모리 스냅샷을 확인할때 당신이 보고있는 것이 무엇인지 쉽게 이해 할수있도록 해준다. -**그렇게 하지 않을 경우:** +**그렇게 하지 않을 경우:** 당신이 익명함수에서 메모리 소비가 많다는 것을 확인 했을 때 코어 덤프(메모리 스냅샷)을 이용해 프로덕션 문제를 디버깅하는 것이 어려울 수도 있습니다.

-## ![✔] 3.6 변수, 상수, 함수, 클래스의 명명 규칙(naming convention) +## ![✔] 3.6 변수, 상수, 함수, 클래스의 명명 규칙(naming convention) **핵심요약:** 상수와 변수 함수를 명명할때는 **_lowerCamelCase_** 를 사용하고 클래스를 명명 할때는 **_UpperCamelCase_**(첫 글자 대문자)를 사용하라. 이것은 일반 변수/함수와 인스턴스로 만들어야 하는 클래스를 구분하는데 도움을 것이다. 설명이 포함된 이름을 사용하되 이름을 짧게 유지하도록 해라. @@ -385,75 +397,106 @@ null == undefined // true # `4. 테스트 및 전체 품질 관리` -## ![✔] 4.1 At the very least, write API (component) testing +## ![✔] 4.1 최소한, API(컴포넌트) 별로 테스트를 만들어라 -**핵심요약:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' run out of control and being abandoned. For that reason, prioritize and start with API testing which is the easiest to write and provide more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/). Afterward, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc +**핵심요약:** 대부분의 프로젝트는 짧은 일정으로 인해 자동화 된 테스트가 없거나 '프로젝트를 테스트하는 것'은 종종 통제를 벗어나서 버려진다. 그런 이유로 우선 순위를 정해서 가장 간단하고 단위 테스트보다 더 넓은 커버리지를 제공하는 API 테스트로 시작해야한다(당신은 심지어 [포스트맨](https://www.getpostman.com/)같은 도구를 이용하여 코드 없이 손수 API테스트를 만들수 있다). 당신이 시간과 자원이 많을때 단위 테스트, DB 테스트, 성능 테스트 등과 같은 고급 유형의 테스트를 계속 진행하면 된다. -**그렇게 하지 않을 경우:** You may spend long days on writing unit tests to find out that you got only 20% system coverage +**그렇게 하지 않을 경우:** 단위 테스트를 만드는 것에 오랜 시간을 투자하였지만 테스트 범위가 전체 시스템의 20% 밖에 안되는것을 확인하게 될수도 있다

-## ![✔] 4.2 Detect code issues with a linter +## ![✔] 4.2 각 테스트 이름에는 3 파트를 포함시켜라 + +**핵심요약:** 테스트를 요구 사항 수준에서 작성하게 하여 내부 코드에 익숙하지 않은 QA 엔지니어 및 개발자도 이해하게 하라. 테스트 이름에 무엇을 테스트 중이고(테스트 중인 단위), 어떤 상황에서 테스트 중인지 그리고 예상되는 결과는 무엇인지 언급하라. -**핵심요약:** Use a code linter to check basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices) on Code Style Practices +**그렇게 하지 않을 경우:** 배포가 실패하더니, '기능 추가'라는 이름의 테스트가 실패했다고 한다. 당신은 정확히 어떤 것에서 오류가 났는지 알 수 있을까? -**그렇게 하지 않을 경우:** You may let pass some anti-pattern and possible vulnerable code to your production environment. +🔗 [**Read More: Include 3 parts in each test name**](/sections/testingandquality/3-parts-in-name.md)

-## ![✔] 4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) +## ![✔] 4.3 린터(linter)를 통해서 코드 이슈를 확인하라 -**핵심요약:** Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of complex setup that demands a steep learning curve. Nowadays, it became much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully +**핵심요약:** 코드 린터를 사용해서 코드의 질을 확인하고 안티패턴을 미리 감지하라. 어떤 테스트든지 그 전에 실행하도록 하고 pre-commit git-hook에 추가하여 리뷰를 하는 시간을 최소화 하고 어떤 이슈든 고쳐지도록 하라. [Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices)의 코드 스타일 역시 참고하라. -**그렇게 하지 않을 경우:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup +**그렇게 하지 않을 경우:** 당신은 안티패턴과 취약한 코드를 상용환경에 노출 시킬 수 있다 -🔗 [**자세히 보기: Choosing CI platform**](/sections/testingandquality/citools.korean.md) +

+ +## ![✔] 4.4 공용으로 고정되어 사용되는 테스트 데이터를 피하고 테스트마다 데이터를 추가하라 + +**핵심요약:** 테스트간의 간섭을 최소화하고 테스트 플로우에 대해 쉽게 추론하려면 각 테스트가 고유한 데이터를 추가하여 작동해야한다. 테스트가 일부 DB 데이터를 가져오거나 가정해야 할 때마다 명시적으로 데이터를 추가해야 하며 다른 레코드의 변경을 피해야 한다. + +**그렇게 하지 않을 경우:** 테스트가 실패하여 배포가 중단되는 시나리오를 생각해보라. 귀중한 시간을 소모하여 조사 끝에 슬픈 결론으로 끝이 난다. "오류 보고서: 시스템은 잘 작동하지만 테스트의 상호 간섭으로 인해 배포 실패" + +🔗 [**Read More: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md) + +

+ +## ![✔] 4.5 의존성의 취약점을 끊임없이 검사하라 + +**핵심요약:** Express와 같은 가장 신뢰할 수있는 의존성 모듈 조차도 알려진 취약점이 있다. 이는 CI에서 빌드시마다 호출하도록 할수있는 🔗 [npm audit](https://docs.npmjs.com/cli/audit) and 🔗 [snyk.io](https://snyk.io)와 같은 커뮤니티 혹은 상업 도구를 사용하여 쉽게 해결할 수 있다. + +**그렇게 하지 않을 경우:** 도구를 사용하지 않고 취약점으로부터 코드를 깨끗하게 유지하려면 새로운 위협에 관한 내용들을 지속적으로 따라야할것이다. 아주 지루하겠지만

-## ![✔] 4.4 Constantly inspect for vulnerable dependencies +## ![✔] 4.6 테스트를 태그하라 (#테스트) -**핵심요약:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [npm audit](https://docs.npmjs.com/cli/audit) and 🔗 [snyk.io](https://snyk.io) that can be invoked from your CI on every build +**핵심요약:** 다른 종류의 테스트는 서로 다른 시나리오에서 실행되어야한다. 빌드가 되는지 안되는지에 대한 테스트, IO가 없는 테스트 등은 개발자가 파일을 저장하거나 커밋 할 때 테스트를 실행해야 하고, 보통은 풀리퀘스트가 있을 때 전체 종단 간 테스트를 실행한다. #cold #api #sanity와 같은 키워드를 사용하여 테스트에 태그를 지정하여 테스트 장치로 가져와서 원하는 하위 집합을 호출 할 수 있다. 예를 들어, 다음은 [Mocha](https://mochajs.org/)와 함께 'sanity' 테스트 그룹을 호출하는 방법이다. mocha --grep 'sanity' -**그렇게 하지 않을 경우:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious +**그렇게 하지 않을 경우:** 수십 개의 DB 쿼리를 수행하는 테스트를 포함하여 모든 테스트를 실행하면 개발자가 작은 변경을 할 때마다 매우 느려질 수 있으므로 테스트 실행을 방해 할 수 있다

-## ![✔] 4.5 Tag your tests +## ![✔] 4.7 테스트 범위를 확인하면 잘못 된 테스트 패턴을 확인하는 것을 도울 수 있다 -**핵심요약:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' +**핵심요약:** [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)와 같은 코드 커버리지 도구는 다음 세 가지 이유로 매우 유용하다. 첫째, 무료다(노력이 전혀 필요 없음). 둘째, 테스트 범위의 감소를 확인하는 데 도움을 준다. 마지막으로 테스트의 불일치를 강조합니다. 색이 입혀진 코드 범위 보고서를 보면서 알아 챘겠지만, 예를 들어 catch 절처럼 테스트되지 않은 코드 영역 (테스트에서는 오류가 발생했을 때의 실제 앱 동작 경로가 아니라 오류가 발생하지 않는 경로 만 호출한다는 의미)을 확인할 수 있다. 커버리지가 특정 임계 값 아래로 떨어지면 빌드 실패로 설정하라. -**그렇게 하지 않을 경우:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests +**그렇게 하지 않을 경우:** 코드의 상당 부분이 테스트에 포함되지 않았다는 것을 알려주는 자동화된 측정 항목은 없다

-## ![✔] 4.6 Check your test coverage, it helps to identify wrong test patterns +## ![✔] 4.8 오래된 패키지를 검사하라 -**핵심요약:** Code coverage tools like [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold +**핵심요약:** 원하는 도구(예: 'npm outdated' 또는 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates))를 사용하여 오래된 패키지를 검사하고 CI 파이프 라인에 검사를 삽입하고, 심각한 시나리오에서는 빌드가 실패하도록 하라. 예를 들어 설치된 패키지가 5 패치 커밋 차이나거나(예: 로컬 버전이 1.3.1이고 저장소 버전이 1.3.8 임) 작성자가 더 이상 사용하지 않는 것으로 태그 지정된 경우에 빌드를 종료시키고 버전을 배포하지 못하게하라. -**그렇게 하지 않을 경우:** There won't be any automated metric telling you when a large portion of your code is not covered by testing +**그렇게 하지 않을 경우:** 당신의 상용 버전은 제작자가 위험하다고 태그한 패키지를 실행하게 될것이다

-## ![✔] 4.7 Inspect for outdated packages +## ![✔] 4.9 e2e 테스트를 위해 docker-compose를 사용하라 -**핵심요약:** Use your preferred tool (e.g. 'npm outdated' or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) to detect installed packages which are outdated, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version +**핵심요약:** 실제 데이터가 포함된 엔드 투 엔드(e2e) 테스트는 DB와 같은 몇가지 무거운 서비스가 포함되어야 하기때문에 취약한 연결고리가 되고는 했다. docker-compose는 간단한 텍스트 파일과 쉬운 명령을 사용하여 상용 환경과 같은 환경을 만들어서 이 문제를 쉽게 만들어준다. 이것을 이용해서 e2e테스트에 필요한 모든 서비스, DB 그리고 격리된 네트워크를 만들 수 있다. 또한 마지막으로 각 테스트 전에 호출되고 바로 사라지는 무상태(stateless) 환경을 유지하게 해준다. -**그렇게 하지 않을 경우:** Your production will run packages that have been explicitly tagged by their author as risky +**그렇게 하지 않을 경우:** docker-compose가 없으면 개발자 머신을 포함한 각 테스트 환경에 대한 테스트 DB를 유지 관리해야하며 모든 DB를 동기화하여 테스트 결과가 환경에 따라 달라지지 않도록 해야한다

-## ![✔] 4.8 Use docker-compose for e2e testing +## ![✔] 4.10 정적 분석 도구를 이용하여 정기적으로 리팩터링하라 + +**핵심요약:** 정적 분석 도구는 객관적인 방법으로 코드 품질을 향상시키고 코드를 유지 관리 가능하도록 도와 준다. CI 빌드에 정적 분석 도구를 추가하여 코드에서 수상한 낌새를 발견하면 실패하도록 할 수 있습니다. 일반 linting에 비해 더 나은 점은 여러 파일의 컨텍스트에서 품질을 검사하거나(예: 중복 검사) 고급 분석을 수행하고(예: 코드 복잡성) 코드 문제의 내역 및 진행 상황을 추적 할 수있다는 것이다. 사용할 수있는 도구의 두 가지 예는 [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube))와 [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate))입니다. + +**그렇게 하지 않을 경우:** 형편없는 코드의 품질로 인해 버그와 성능은 반짝이는 새로운 라이브러리나 최첨단 기술로는 고칠수 없는 문제가 되어버릴 것이다 + +🔗 [**Read More: Refactoring!**](/sections/testingandquality/refactoring.md) + +

+ +## ![✔] 4.11 CI 플랫폼을 잘 선택하라 (Jenkins vs CircleCI vs Travis vs 기타 다른 모든 것들) + +**핵심요약:** 지속적 통합 플랫폼(CICD)은 모든 품질 도구들(test, lint 등)을 호스팅할것이기 때문에 다양한 생태계의 플러그인을 가지고 있어야 한다. Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. 예전에는 학습이 어렵고 복잡한 설정을 요구하는 [Jenkins](https://jenkins.io/)가 가장 큰 커뮤니티를 보유하고 있고 매우 강력한 플랫폼을 갖추고 있기 때문에 대부분의 프로젝트에서 기본 선택이었다. 요즘에는 [CircleCI](https://circleci.com)와 같은 SaaS 도구를 사용하여 CI 솔루션을 설정하는 것이 훨씬 쉬워졌다. 이러한 도구를 사용하면 전체 인프라 관리 부담없이 유연한 CI 파이프 라인을 만들 수 있다. 결국은 견고성과 속도 사이의 트레이드 오프라고 할수있다. -**핵심요약:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Docker-compose turns this problem into a breeze by crafting production-like environment using a simple text file and easy commands. It allows crafting all the dependent services, DB and isolated network for e2e testing. Last but not least, it can keep a stateless environment that is invoked before each test suite and dies right after +**그렇게 하지 않을 경우:** CircleCI와 같은 중소 솔루션 업체를 선택하면 커스터마이즈가 많이 필요할 때 힘들 수 있다. 반대로 Jenkins를 사용하면 인프라 설치에 시간을 많이 소모하게 될 수 있다. -**그렇게 하지 않을 경우:** Without docker-compose teams must maintain a testing DB for each testing environment including developers machines, keep all those DBs in sync so test results won't vary across environments +🔗 [**자세히 보기: Choosing CI platform**](/sections/testingandquality/citools.korean.md)


-

⬆ 목차로 돌아가기

-# `5. 운영 환경으로 전환하기` +

⬆ Return to top

+ +# `5. Going To Production Practices` ## ![✔] 5.1. Monitoring! @@ -461,7 +504,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Failure === disappointed customers. Simple -🔗 [**자세히 보기: Monitoring!**](/sections/production/monitoring.korean.md) +🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md)

@@ -469,9 +512,9 @@ null == undefined // true **핵심요약:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted -**그렇게 하지 않을 경우:** You end-up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +**그렇게 하지 않을 경우:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information -🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.korean.md) +🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md)

@@ -481,37 +524,37 @@ null == undefined // true **그렇게 하지 않을 경우:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly -🔗 [**자세히 보기: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.korean.md) +🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md)

## ![✔] 5.4. Lock dependencies -**핵심요약:** Your code must be identical across all environments, but amazingly NPM lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using NPM config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grain control use NPM” shrinkwrap”. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default +**핵심요약:** Your code must be identical across all environments, but amazingly npm lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using npm config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grained control use `npm shrinkwrap`. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default -**그렇게 하지 않을 경우:** QA will thoroughly test the code and approve a version that will behave differently at production. Even worse, different servers at the same production cluster might run different code +**그렇게 하지 않을 경우:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code -🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.korean.md) +🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md)

## ![✔] 5.5. Guard process uptime using the right tool -**핵심요약:** The process must go on and get restarted upon failures. For simple scenarios, ‘restarter’ tools like PM2 might be enough but in today ‘dockerized’ world – a cluster management tools should be considered as well +**핵심요약:** The process must go on and get restarted upon failures. For simple scenarios, process management tools like PM2 might be enough but in today's ‘dockerized’ world, cluster management tools should be considered as well -**그렇게 하지 않을 경우:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to a DevOps chaos +**그렇게 하지 않을 경우:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos -🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.korean.md) +🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md)

## ![✔] 5.6. Utilize all CPU cores -**핵심요약:** At its basic form, a Node app runs on a single CPU core while all other are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) +**핵심요약:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) **그렇게 하지 않을 경우:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) -🔗 [**자세히 보기: Utilize all CPU cores**](/sections/production/utilizecpu.korean.md) +🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md)

@@ -521,17 +564,17 @@ null == undefined // true **그렇게 하지 않을 경우:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes -🔗 [**자세히 보기: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.korean.md) +🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md)

## ![✔] 5.8. Discover errors and downtime using APM products -**핵심요약:** Monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause +**핵심요약:** Application monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause -**그렇게 하지 않을 경우:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affects the UX +**그렇게 하지 않을 경우:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX -🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.korean.md) +🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md)

@@ -541,154 +584,517 @@ null == undefined // true **그렇게 하지 않을 경우:** A world champion IT/DevOps guy won’t save a system that is badly written -🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productioncode.md) +🔗 [**Read More: Make your code production-ready**](/sections/production/productioncode.md)

## ![✔] 5.10. Measure and guard the memory usage -**핵심요약:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leaks memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large app consider baking your memory watch into a robust monitoring system +**핵심요약:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system **그렇게 하지 않을 경우:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**자세히 보기: Measure and guard the memory usage**](/sections/production/measurememory.korean.md) +🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md)

## ![✔] 5.11. Get your frontend assets out of Node -**핵심요약:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single threaded model +**핵심요약:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single-threaded model **그렇게 하지 않을 경우:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content -🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.korean.md) +🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md)

-## ![✔] 5.12. Be stateless, kill your Servers almost every day +## ![✔] 5.12. Be stateless, kill your servers almost every day -**핵심요약:** Store any type of data (e.g. users session, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior +**핵심요약:** Store any type of data (e.g. user sessions, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior **그렇게 하지 않을 경우:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server -🔗 [**자세히 보기: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.korean.md) +🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md)

## ![✔] 5.13. Use tools that automatically detect vulnerabilities -**핵심요약:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can get easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**핵심요약:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately -**그렇게 하지 않을 경우:** 그렇게 하지 않을 경우: Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious +**그렇게 하지 않을 경우:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious -🔗 [**자세히 보기: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.korean.md) +🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md)

-## ![✔] 5.14. Assign ‘TransactionId’ to each log statement +## ![✔] 5.14. Assign a transaction id to each log statement **핵심요약:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside **그렇게 하지 않을 경우:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue -🔗 [**자세히 보기: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.korean.md) +🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md)

## ![✔] 5.15. Set NODE_ENV=production -**핵심요약:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many NPM packages determining the current environment and optimize their code for production +**핵심요약:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many npm packages determine the current environment and optimize their code for production -**그렇게 하지 않을 경우:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes the slower by a factor of three! +**그렇게 하지 않을 경우:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! -🔗 [**자세히 보기: Set NODE_ENV=production**](/sections/production/setnodeenv.korean.md) +🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md)

## ![✔] 5.16. Design automated, atomic and zero-downtime deployments -**TL;DR:** Researches show that teams who perform many deployments – lowers the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improves the deployment process. You should probably achieve that using Docker combined with CI tools as they became the industry standard for streamlined deployment +**핵심요약:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment -**그렇게 하지 않을 경우:** Long deployments -> production down time & human-related error -> team unconfident and in making deployment -> less deployments and features +**그렇게 하지 않을 경우:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features

## ![✔] 5.17. Use an LTS release of Node.js -**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements +**핵심요약:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements **그렇게 하지 않을 경우:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain -🔗 [**자세히 보기: Use an LTS release of Node.js**](/sections/production/LTSrelease.korean.md) +🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md) + +

+ +## ![✔] 5.18. Don't route logs within the app + +**핵심요약:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). + +**그렇게 하지 않을 경우:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns + +🔗 [**Read More: Log Routing**](/sections/production/logrouting.md)


-

⬆ 목차로 돌아가기

+

⬆ Return to top

+ +# `6. Security Best Practices` + +
+54 items +
+ +## ![✔] 6.1. Embrace linter security rules + + + +**핵심요약:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter + +**그렇게 하지 않을 경우:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories + +🔗 [**Read More: Lint rules**](/sections/security/lintrules.md) + +

+ +## ![✔] 6.2. Limit concurrent requests using a middleware + + + +**핵심요약:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) + +**그렇게 하지 않을 경우:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. + +🔗 [**Read More: Implement rate limiting**](/sections/security/limitrequests.md) + +

+ +## ![✔] 6.3 Extract secrets from config files or use packages to encrypt them + + + +**핵심요약:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally + +**그렇게 하지 않을 경우:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). + +🔗 [**Read More: Secret management**](/sections/security/secretmanagement.md) + +

+ +## ![✔] 6.4. Prevent query injection vulnerabilities with ORM/ODM libraries + + + +**핵심요약:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. + +**그렇게 하지 않을 경우:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. + +🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md) + +

+ +## ![✔] 6.5. Collection of generic security best practices + +**핵심요약:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Common security best practices**](/sections/security/commonsecuritybestpractices.md) + +

+ +## ![✔] 6.6. Adjust the HTTP response headers for enhanced security + + + +**핵심요약:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). + +**그렇게 하지 않을 경우:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities + +🔗 [**Read More: Using secure headers in your application**](/sections/security/secureheaders.md) + +

+ +## ![✔] 6.7. Constantly and automatically inspect for vulnerable dependencies + + + +**핵심요약:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. + +**그렇게 하지 않을 경우:** An attacker could detect your web framework and attack all its known vulnerabilities. + +🔗 [**Read More: Dependency security**](/sections/security/dependencysecurity.md) + +

+ +## ![✔] 6.8. Avoid using the Node.js crypto library for handling passwords, use Bcrypt + + + +**핵심요약:** Passwords or secrets (API keys) should be stored using a secure hash + salt function like `bcrypt`, that should be a preferred choice over its JavaScript implementation due to performance and security reasons. + +**그렇게 하지 않을 경우:** Passwords or secrets that are persisted without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. + +🔗 [**Read More: Use Bcrypt**](/sections/security/bcryptpasswords.md) + +

+ +## ![✔] 6.9. Escape HTML, JS and CSS output + + + +**핵심요약:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) + +**그렇게 하지 않을 경우:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients + +🔗 [**Read More: Escape output**](/sections/security/escape-output.md) + +

+ +## ![✔] 6.10. Validate incoming JSON schemas + + + +**핵심요약:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) + +**그렇게 하지 않을 경우:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application + +🔗 [**Read More: Validate incoming JSON schemas**](/sections/security/validation.md) + +

+ +## ![✔] 6.11. Support blacklisting JWTs + + + +**핵심요약:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blacklist of untrusted tokens that are validated on each request. + +**그렇게 하지 않을 경우:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. + +🔗 [**Read More: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md) + +

+ +## ![✔] 6.12. Prevent brute-force attacks against authorization -# `보안` + -## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까? +**핵심요약:** A simple and powerful technique is to limit authorization attempts using two metrics: + +1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. +2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. +**그렇게 하지 않을 경우:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application + +🔗 [**Read More: Login rate limiting**](/sections/security/login-rate-limit.md) + +

+ +## ![✔] 6.13. Run Node.js as non-root user + + + +**핵심요약:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" + +**그렇게 하지 않을 경우:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to his server) + +🔗 [**Read More: Run Node.js as non-root user**](/sections/security/non-root-user.md) + +

+ +## ![✔] 6.14. Limit payload size using a reverse-proxy or a middleware + + + +**핵심요약:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads + +**그렇게 하지 않을 경우:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks + +🔗 [**Read More: Limit payload size**](/sections/security/requestpayloadsizelimit.md) + +

+ +## ![✔] 6.15. Avoid JavaScript eval statements + + + +**핵심요약:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. + +**그렇게 하지 않을 경우:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. + +🔗 [**Read More: Avoid JavaScript eval statements**](/sections/security/avoideval.md) + +

+ +## ![✔] 6.16. Prevent evil RegEx from overloading your single thread execution + + + +**핵심요약:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns + +**그렇게 하지 않을 경우:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 + +🔗 [**Read More: Prevent malicious RegEx**](/sections/security/regex.md) + +

+ +## ![✔] 6.17. Avoid module loading using a variable + + + +**핵심요약:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough + +**그렇게 하지 않을 경우:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the filesystem, or access already existing system files. + +🔗 [**Read More: Safe module loading**](/sections/security/safemoduleloading.md) + +

+ +## ![✔] 6.18. Run unsafe code in a sandbox + + + +**핵심요약:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox + +**그렇게 하지 않을 경우:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables + +🔗 [**Read More: Run unsafe code in a sandbox**](/sections/security/sandbox.md) + +

+ +## ![✔] 6.19. Take extra care when working with child processes + + + +**핵심요약:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. + +**그렇게 하지 않을 경우:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. + +🔗 [**Read More: Be cautious when working with child processes**](/sections/security/childprocesses.md) + +

+ +## ![✔] 6.20. Hide error details from clients + + + +**핵심요약:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details + +**그렇게 하지 않을 경우:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace + +🔗 [**Read More: Hide error details from client**](/sections/security/hideerrors.md) + +

+ +## ![✔] 6.21. Configure 2FA for npm or Yarn + + + +**핵심요약:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. + +**그렇게 하지 않을 경우:** [Have you heard about the eslint developer who's password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) + +

+ +## ![✔] 6.22. Modify session middleware settings + + + +**핵심요약:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) + +**그렇게 하지 않을 경우:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities + +🔗 [**Read More: Cookie and session security**](/sections/security/sessions.md) + +

+ +## ![✔] 6.23. Avoid DOS attacks by explicitly setting when a process should crash + + + +**핵심요약:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) + +**그렇게 하지 않을 경우:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease + +

+ +## ![✔] 6.24. Prevent unsafe redirects + + + +**핵심요약:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. + +**그렇게 하지 않을 경우:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. + +🔗 [**Read More: Prevent unsafe redirects**](/sections/security/saferedirects.md) + +

+ +## ![✔] 6.25. Avoid publishing secrets to the npm registry + + + +**핵심요약:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. + +**그렇게 하지 않을 경우:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. + +🔗 [**Read More: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md)


-# `성능` +

⬆ Return to top

-## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까? +# `7. Performance Best Practices` + +## Our contributors are working on this section. [Would you like to join?](https://github.com/i0natan/nodebestpractices/issues/256) + +## ![✔] 7.1. Prefer native JS methods over user-land utils like Lodash + + **핵심요약:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. + Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. + +**그렇게 하지 않을 경우:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. + +🔗 [**Read More: Native over user land utils**](/sections/performance/nativeoverutil.md)


-# 마일스톤 +# Milestones -이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. 만약 이 프로젝트에 기여를 하고 싶으시면 [마일스톤](https://github.com/i0natan/nodebestpractices/milestones) 을 보고 참여하십시오. +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project -

+
-## 번역 +## Translations -모든 번역은 커뮤니티에 의해 기여되고 있습니다. 이미 완성된 번역이나, 진행중, 새로운 번역에 대한 도움은 언제나 환영합니다! +All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! -### 번역 작업 완료 +### Completed translations -* ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) +- ![BR](/assets/flags/BR.png) [Brazilian Portuguese](/README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) +- ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) -### 번역 작업중 +### Translations in progress +<<<<<<< HEAD * ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) * ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) * ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) * ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) * ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) * ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +======= +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) +- ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +>>>>>>> master -


+

-# Contributors +## Steering Committee -## `Yoni Goldberg` +Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/i0natan/nodebestpractices/projects). -Independent Node.js consultant who works with customers in USA, Europe, and Israel on building large-scale scalable Node applications. Many of the best practices above were first published in his blog post at [http://www.goldbergyoni.com](http://www.goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com + -## `Ido Richter` +[Yoni Goldberg](https://github.com/i0natan) + + -👨‍💻 Software engineer, 🌐 web developer, 🤖 emojis enthusiast +Independent Node.js consultant who works with customers in USA, Europe, and Israel on building large scale scalable Node applications. Many of the best practices above were first published at [goldbergyoni.com](https://goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com -## `Refael Ackermann` [@refack](https://github.com/refack) <refack@gmail.com> (he/him) +
-Node.js Core Collaborator, been noding since 0.4, and have noded in multiple production sites. Founded `node4good` home of [`lodash-contrib`](https://github.com/node4good/lodash-contrib), [`formage`](https://github.com/node4good/formage), and [`asynctrace`](https://github.com/node4good/asynctrace). -`refack` on freenode, Twitter, GitHub, GMail, and many other platforms. DMs are open, happy to help + -## `Bruno Scheufler` +[Bruno Scheufler](https://github.com/BrunoScheufler) + -💻 full-stack web developer and Node.js enthusiast +💻 full-stack web engineer, Node.js & GraphQL enthusiast -## `Kyle Martin` [@js-kyle](https://github.com/js-kyle) -Full Stack Developer based in New Zealand, interested in architecting and building Node.js applications to perform at global scale. Keen contributor to open source software, including Node.js Core. +
-


+ + +[Kyle Martin](https://github.com/js-kyle) + + + +Full Stack Developer & Site Reliability Engineer based in New Zealand, interested in web application security, and architecting and building Node.js applications to perform at global scale. + +
+ + + +[Sagir Khan](https://github.com/sagirk) + + + + +Deep specialist in JavaScript and its ecosystem — React, Node.js, MongoDB, pretty much anything that involves using JavaScript/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation, collaborating on the Community Committee's Website Redesign Initiative. + +
+ +## Collaborators + +Thank you to all our collaborators! 🙏 -# Thank You Notes +Our collaborators are members who are contributing to the repository on a reguar basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -This repository is being kept up to date thanks to the help from the community. We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A 🌻 marks a successful pull request and a ⭐ marks an approved new best practice +| | | +| :--: | :--: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | + +### Past collaborators + +| | +| :--: | +| [Refael Ackermann](https://github.com/refack) | + +
+ +## Thank You Notes + +We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A 🌻 marks a successful pull request and a ⭐ marks an approved new best practice. ### Flowers @@ -767,7 +1173,12 @@ This repository is being kept up to date thanks to the help from the community. 🌻 [Ryan Ouyang](https://github.com/ryanouyang), 🌻 [Gabriel Lidenor](https://github.com/GabrielLidenor), 🌻 [Roman](https://github.com/animir), -🌻 [Francozeira](https://github.com/Francozeira) +🌻 [Invvard](https://github.com/Invvard), +🌻 [Rômulo Garofalo](https://github.com/romulogarofalo), +🌻 [Tho Q Luong](https://github.com/thoqbk), +🌻 [Burak Shen](https://github.com/Qeneke), +🌻 [Martin Muzatko](https://github.com/MartinMuzatko) + ### Stars From d9c33c451aa5faae8ce5f5f25e80fee3d0a1f231 Mon Sep 17 00:00:00 2001 From: Sangbeom Han Date: Mon, 3 Jun 2019 22:34:43 +0900 Subject: [PATCH 0014/1795] Translate tedious words 'Read more' --- README.korean.md | 88 ++++++++++++++++++++++++------------------------ 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/README.korean.md b/README.korean.md index ca36282e3..62f2263f2 100644 --- a/README.korean.md +++ b/README.korean.md @@ -411,7 +411,7 @@ null == undefined // true **그렇게 하지 않을 경우:** 배포가 실패하더니, '기능 추가'라는 이름의 테스트가 실패했다고 한다. 당신은 정확히 어떤 것에서 오류가 났는지 알 수 있을까? -🔗 [**Read More: Include 3 parts in each test name**](/sections/testingandquality/3-parts-in-name.md) +🔗 [**자세히 보기: Include 3 parts in each test name**](/sections/testingandquality/3-parts-in-name.md)

@@ -429,7 +429,7 @@ null == undefined // true **그렇게 하지 않을 경우:** 테스트가 실패하여 배포가 중단되는 시나리오를 생각해보라. 귀중한 시간을 소모하여 조사 끝에 슬픈 결론으로 끝이 난다. "오류 보고서: 시스템은 잘 작동하지만 테스트의 상호 간섭으로 인해 배포 실패" -🔗 [**Read More: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md) +🔗 [**자세히 보기: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md)

@@ -479,7 +479,7 @@ null == undefined // true **그렇게 하지 않을 경우:** 형편없는 코드의 품질로 인해 버그와 성능은 반짝이는 새로운 라이브러리나 최첨단 기술로는 고칠수 없는 문제가 되어버릴 것이다 -🔗 [**Read More: Refactoring!**](/sections/testingandquality/refactoring.md) +🔗 [**자세히 보기: Refactoring!**](/sections/testingandquality/refactoring.md)

@@ -504,7 +504,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Failure === disappointed customers. Simple -🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md) +🔗 [**자세히 보기: Monitoring!**](/sections/production/monitoring.md)

@@ -514,7 +514,7 @@ null == undefined // true **그렇게 하지 않을 경우:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information -🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md) +🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.md)

@@ -524,7 +524,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly -🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md) +🔗 [**자세히 보기: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md)

@@ -534,7 +534,7 @@ null == undefined // true **그렇게 하지 않을 경우:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code -🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md) +🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.md)

@@ -544,7 +544,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos -🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md) +🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.md)

@@ -554,7 +554,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) -🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md) +🔗 [**자세히 보기: Utilize all CPU cores**](/sections/production/utilizecpu.md)

@@ -564,7 +564,7 @@ null == undefined // true **그렇게 하지 않을 경우:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes -🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md) +🔗 [**자세히 보기: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md)

@@ -574,7 +574,7 @@ null == undefined // true **그렇게 하지 않을 경우:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX -🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md) +🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.md)

@@ -584,7 +584,7 @@ null == undefined // true **그렇게 하지 않을 경우:** A world champion IT/DevOps guy won’t save a system that is badly written -🔗 [**Read More: Make your code production-ready**](/sections/production/productioncode.md) +🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productioncode.md)

@@ -594,7 +594,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md) +🔗 [**자세히 보기: Measure and guard the memory usage**](/sections/production/measurememory.md)

@@ -604,7 +604,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content -🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md) +🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.md)

@@ -614,7 +614,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server -🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md) +🔗 [**자세히 보기: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md)

@@ -624,7 +624,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious -🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md) +🔗 [**자세히 보기: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md)

@@ -634,7 +634,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue -🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md) +🔗 [**자세히 보기: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md)

@@ -644,7 +644,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! -🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md) +🔗 [**자세히 보기: Set NODE_ENV=production**](/sections/production/setnodeenv.md)

@@ -662,7 +662,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain -🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md) +🔗 [**자세히 보기: Use an LTS release of Node.js**](/sections/production/LTSrelease.md)

@@ -672,7 +672,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns -🔗 [**Read More: Log Routing**](/sections/production/logrouting.md) +🔗 [**자세히 보기: Log Routing**](/sections/production/logrouting.md)


@@ -692,7 +692,7 @@ null == undefined // true **그렇게 하지 않을 경우:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories -🔗 [**Read More: Lint rules**](/sections/security/lintrules.md) +🔗 [**자세히 보기: Lint rules**](/sections/security/lintrules.md)

@@ -704,7 +704,7 @@ null == undefined // true **그렇게 하지 않을 경우:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. -🔗 [**Read More: Implement rate limiting**](/sections/security/limitrequests.md) +🔗 [**자세히 보기: Implement rate limiting**](/sections/security/limitrequests.md)

@@ -716,7 +716,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). -🔗 [**Read More: Secret management**](/sections/security/secretmanagement.md) +🔗 [**자세히 보기: Secret management**](/sections/security/secretmanagement.md)

@@ -728,7 +728,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. -🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md) +🔗 [**자세히 보기: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md)

@@ -736,7 +736,7 @@ null == undefined // true **핵심요약:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. -🔗 [**Read More: Common security best practices**](/sections/security/commonsecuritybestpractices.md) +🔗 [**자세히 보기: Common security best practices**](/sections/security/commonsecuritybestpractices.md)

@@ -748,7 +748,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities -🔗 [**Read More: Using secure headers in your application**](/sections/security/secureheaders.md) +🔗 [**자세히 보기: Using secure headers in your application**](/sections/security/secureheaders.md)

@@ -760,7 +760,7 @@ null == undefined // true **그렇게 하지 않을 경우:** An attacker could detect your web framework and attack all its known vulnerabilities. -🔗 [**Read More: Dependency security**](/sections/security/dependencysecurity.md) +🔗 [**자세히 보기: Dependency security**](/sections/security/dependencysecurity.md)

@@ -772,7 +772,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Passwords or secrets that are persisted without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. -🔗 [**Read More: Use Bcrypt**](/sections/security/bcryptpasswords.md) +🔗 [**자세히 보기: Use Bcrypt**](/sections/security/bcryptpasswords.md)

@@ -784,7 +784,7 @@ null == undefined // true **그렇게 하지 않을 경우:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients -🔗 [**Read More: Escape output**](/sections/security/escape-output.md) +🔗 [**자세히 보기: Escape output**](/sections/security/escape-output.md)

@@ -796,7 +796,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application -🔗 [**Read More: Validate incoming JSON schemas**](/sections/security/validation.md) +🔗 [**자세히 보기: Validate incoming JSON schemas**](/sections/security/validation.md)

@@ -808,7 +808,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. -🔗 [**Read More: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md) +🔗 [**자세히 보기: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md)

@@ -823,7 +823,7 @@ null == undefined // true **그렇게 하지 않을 경우:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application -🔗 [**Read More: Login rate limiting**](/sections/security/login-rate-limit.md) +🔗 [**자세히 보기: Login rate limiting**](/sections/security/login-rate-limit.md)

@@ -835,7 +835,7 @@ null == undefined // true **그렇게 하지 않을 경우:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to his server) -🔗 [**Read More: Run Node.js as non-root user**](/sections/security/non-root-user.md) +🔗 [**자세히 보기: Run Node.js as non-root user**](/sections/security/non-root-user.md)

@@ -847,7 +847,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks -🔗 [**Read More: Limit payload size**](/sections/security/requestpayloadsizelimit.md) +🔗 [**자세히 보기: Limit payload size**](/sections/security/requestpayloadsizelimit.md)

@@ -859,7 +859,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. -🔗 [**Read More: Avoid JavaScript eval statements**](/sections/security/avoideval.md) +🔗 [**자세히 보기: Avoid JavaScript eval statements**](/sections/security/avoideval.md)

@@ -871,7 +871,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 -🔗 [**Read More: Prevent malicious RegEx**](/sections/security/regex.md) +🔗 [**자세히 보기: Prevent malicious RegEx**](/sections/security/regex.md)

@@ -883,7 +883,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the filesystem, or access already existing system files. -🔗 [**Read More: Safe module loading**](/sections/security/safemoduleloading.md) +🔗 [**자세히 보기: Safe module loading**](/sections/security/safemoduleloading.md)

@@ -895,7 +895,7 @@ null == undefined // true **그렇게 하지 않을 경우:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables -🔗 [**Read More: Run unsafe code in a sandbox**](/sections/security/sandbox.md) +🔗 [**자세히 보기: Run unsafe code in a sandbox**](/sections/security/sandbox.md)

@@ -907,7 +907,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. -🔗 [**Read More: Be cautious when working with child processes**](/sections/security/childprocesses.md) +🔗 [**자세히 보기: Be cautious when working with child processes**](/sections/security/childprocesses.md)

@@ -919,7 +919,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace -🔗 [**Read More: Hide error details from client**](/sections/security/hideerrors.md) +🔗 [**자세히 보기: Hide error details from client**](/sections/security/hideerrors.md)

@@ -941,7 +941,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities -🔗 [**Read More: Cookie and session security**](/sections/security/sessions.md) +🔗 [**자세히 보기: Cookie and session security**](/sections/security/sessions.md)

@@ -963,7 +963,7 @@ null == undefined // true **그렇게 하지 않을 경우:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. -🔗 [**Read More: Prevent unsafe redirects**](/sections/security/saferedirects.md) +🔗 [**자세히 보기: Prevent unsafe redirects**](/sections/security/saferedirects.md)

@@ -975,7 +975,7 @@ null == undefined // true **그렇게 하지 않을 경우:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. -🔗 [**Read More: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md) +🔗 [**자세히 보기: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md)


⬆ Return to top

@@ -991,7 +991,7 @@ null == undefined // true **그렇게 하지 않을 경우:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. -🔗 [**Read More: Native over user land utils**](/sections/performance/nativeoverutil.md) +🔗 [**자세히 보기: Native over user land utils**](/sections/performance/nativeoverutil.md)


From 3c760d20170739435ad059c82ecd43eb135eabc5 Mon Sep 17 00:00:00 2001 From: Sangbeom Han Date: Tue, 9 Jul 2019 19:48:20 +0900 Subject: [PATCH 0015/1795] Translate into Korean to bullet 5.10 This will also delete unnecessary line. --- README.korean.md | 828 +++-------------------------------------------- 1 file changed, 40 insertions(+), 788 deletions(-) diff --git a/README.korean.md b/README.korean.md index f46cc3899..d48632abe 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1,4 +1,4 @@ -<<<<<<< HEAD +<<<<<<< HEAD [✔]: assets/images/checkbox-small-blue.png # Node.js 모범 사례 @@ -444,7 +444,7 @@ null == undefined // true ## ![✔] 4.6 테스트를 태그하라 (#테스트) -**핵심요약:** 다른 종류의 테스트는 서로 다른 시나리오에서 실행되어야한다. 빌드가 되는지 안되는지에 대한 테스트, IO가 없는 테스트 등은 개발자가 파일을 저장하거나 커밋 할 때 테스트를 실행해야 하고, 보통은 풀리퀘스트가 있을 때 전체 종단 간 테스트를 실행한다. #cold #api #sanity와 같은 키워드를 사용하여 테스트에 태그를 지정하여 테스트 장치로 가져와서 원하는 하위 집합을 호출 할 수 있다. 예를 들어, 다음은 [Mocha](https://mochajs.org/)와 함께 'sanity' 테스트 그룹을 호출하는 방법이다. mocha --grep 'sanity' +**핵심요약:** 다른 종류의 테스트는 서로 다른 시나리오에서 실행되어야한다. 빌드가 되는지 안되는지에 대한 테스트, IO가 없는 테스트 등은 개발자가 파일을 저장하거나 커밋 할 때 테스트를 실행해야 하고, 보통은 풀리퀘스트가 있을 때 전체 종단 간 테스트를 실행한다. #cold #api #sanity와 같은 키워드를 사용하여 테스트에 태그를 지정하여 테스트 장치로 가져와서 원하는 하위 집합을 호출 할 수 있다. 예를 들어, 다음은 [Mocha](https://mochajs.org/)와 함께 'sanity' 테스트 그룹을 호출하는 방법이다. `mocha --grep 'sanity'` **그렇게 하지 않을 경우:** 수십 개의 DB 쿼리를 수행하는 테스트를 포함하여 모든 테스트를 실행하면 개발자가 작은 변경을 할 때마다 매우 느려질 수 있으므로 테스트 실행을 방해 할 수 있다 @@ -452,7 +452,7 @@ null == undefined // true ## ![✔] 4.7 테스트 범위를 확인하면 잘못 된 테스트 패턴을 확인하는 것을 도울 수 있다 -**핵심요약:** [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)와 같은 코드 커버리지 도구는 다음 세 가지 이유로 매우 유용하다. 첫째, 무료다(노력이 전혀 필요 없음). 둘째, 테스트 범위의 감소를 확인하는 데 도움을 준다. 마지막으로 테스트의 불일치를 강조합니다. 색이 입혀진 코드 범위 보고서를 보면서 알아 챘겠지만, 예를 들어 catch 절처럼 테스트되지 않은 코드 영역 (테스트에서는 오류가 발생했을 때의 실제 앱 동작 경로가 아니라 오류가 발생하지 않는 경로 만 호출한다는 의미)을 확인할 수 있다. 커버리지가 특정 임계 값 아래로 떨어지면 빌드 실패로 설정하라. +**핵심요약:** [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)와 같은 코드 커버리지 도구는 다음 세 가지 이유로 매우 유용하다. 첫째, 무료다(노력이 전혀 필요 없음). 둘째, 테스트 범위의 감소를 확인하는 데 도움을 준다. 마지막으로 테스트의 불일치를 강조합니다. 색이 입혀진 코드 범위 보고서를 보면서 알아 챘겠지만, 예를 들어 catch 절처럼 테스트되지 않은 코드 영역 (테스트에서는 오류가 발생했을 때의 실제 앱 동작 경로가 아니라 오류가 발생하지 않는 경로 만 호출한다는 의미)을 확인할 수 있다. 커버리지가 특정 임계 값 아래로 떨어지면 빌드 실패로 설정하라 **그렇게 하지 않을 경우:** 코드의 상당 부분이 테스트에 포함되지 않았다는 것을 알려주는 자동화된 측정 항목은 없다 @@ -460,7 +460,7 @@ null == undefined // true ## ![✔] 4.8 오래된 패키지를 검사하라 -**핵심요약:** 원하는 도구(예: 'npm outdated' 또는 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates))를 사용하여 오래된 패키지를 검사하고 CI 파이프 라인에 검사를 삽입하고, 심각한 시나리오에서는 빌드가 실패하도록 하라. 예를 들어 설치된 패키지가 5 패치 커밋 차이나거나(예: 로컬 버전이 1.3.1이고 저장소 버전이 1.3.8 임) 작성자가 더 이상 사용하지 않는 것으로 태그 지정된 경우에 빌드를 종료시키고 버전을 배포하지 못하게하라. +**핵심요약:** 원하는 도구(예: 'npm outdated' 또는 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates))를 사용하여 오래된 패키지를 검사하고 CI 파이프 라인에 검사를 삽입하고, 심각한 시나리오에서는 빌드가 실패하도록 하라. 예를 들어 설치된 패키지가 5 패치 커밋 차이나거나(예: 로컬 버전이 1.3.1이고 저장소 버전이 1.3.8 임) 작성자가 더 이상 사용하지 않는 것으로 태그 지정된 경우에 빌드를 종료시키고 버전을 배포하지 못하게하라 **그렇게 하지 않을 경우:** 당신의 상용 버전은 제작자가 위험하다고 태그한 패키지를 실행하게 될것이다 @@ -468,7 +468,7 @@ null == undefined // true ## ![✔] 4.9 e2e 테스트를 위해 docker-compose를 사용하라 -**핵심요약:** 실제 데이터가 포함된 엔드 투 엔드(e2e) 테스트는 DB와 같은 몇가지 무거운 서비스가 포함되어야 하기때문에 취약한 연결고리가 되고는 했다. docker-compose는 간단한 텍스트 파일과 쉬운 명령을 사용하여 상용 환경과 같은 환경을 만들어서 이 문제를 쉽게 만들어준다. 이것을 이용해서 e2e테스트에 필요한 모든 서비스, DB 그리고 격리된 네트워크를 만들 수 있다. 또한 마지막으로 각 테스트 전에 호출되고 바로 사라지는 무상태(stateless) 환경을 유지하게 해준다. +**핵심요약:** 실제 데이터가 포함된 엔드 투 엔드(e2e) 테스트는 DB와 같은 몇가지 무거운 서비스가 포함되어야 하기때문에 취약한 연결고리가 되고는 했다. docker-compose는 간단한 텍스트 파일과 쉬운 명령을 사용하여 상용 환경과 같은 환경을 만들어서 이 문제를 쉽게 만들어준다. 이것을 이용해서 e2e테스트에 필요한 모든 서비스, DB 그리고 격리된 네트워크를 만들 수 있다. 또한 마지막으로 각 테스트 전에 호출되고 바로 사라지는 무상태(stateless) 환경을 유지하게 해준다 **그렇게 하지 않을 경우:** docker-compose가 없으면 개발자 머신을 포함한 각 테스트 환경에 대한 테스트 DB를 유지 관리해야하며 모든 DB를 동기화하여 테스트 결과가 환경에 따라 달라지지 않도록 해야한다 @@ -497,105 +497,105 @@ null == undefined // true

⬆ Return to top

-# `5. Going To Production Practices` +# `5. 운영환경으로 전환하기` -## ![✔] 5.1. Monitoring! +## ![✔] 5.1. 모니터링! -**핵심요약:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions +**핵심요약:** 모니터링은 고객이 문제를 발견하기 전에 먼저 발견하는 게임이다. 모니터링에는 분명히 전례가없는 중요성을 부여해야한다. 솔루션에 너무 많은 기능들이 들어가 있을 가능성이 있으므로 확인해야만하는 기본 항목을 내부적으로 정의하고 나서 추가적인 기능들을 살펴보고 필요한 기능들이 모두 들어있는 솔루션을 선택하라. 아래의 'gist'를 클릭하면 솔루션 개요를 볼 수 있다 -**그렇게 하지 않을 경우:** Failure === disappointed customers. Simple +**그렇게 하지 않을 경우:** 오류 === 고객의 실망. 간단하다 -🔗 [**자세히 보기: Monitoring!**](/sections/production/monitoring.md) +🔗 [**자세히 보기: 모니터링!**](/sections/production/monitoring.md)

-## ![✔] 5.2. Increase transparency using smart logging +## ![✔] 5.2. 똑똑한 로깅을 통해 투명성을 높여라 -**핵심요약:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted +**핵심요약:** 로그는 디버그 텍스트가 모여있는 의미없는 창고일 수도 있고 앱의 상태를 대시보드로 볼수 있도록 만들어 줄 수 있는 유용한 데이터일 수도 있다. 첫날부터 로그를 어떻게 수집, 저장 및 분석하는지 계획하여 원하는 정보(예: 오류율, 서비스 및 서버를 통한 전체 트랜잭션 수행)를 실제로 추출 할지 확실히 하라 -**그렇게 하지 않을 경우:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +**그렇게 하지 않을 경우:** 당신은 결국 추측하기 힘든 블랙박스에 도달하게고, 필요한 정보를 추가하기 위해 모든 로그를 다시 작성하게 될것이다 🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.md)

-## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy +## ![✔] 5.3. 가능한 모든 것들(예: gzip, SSL)을 reverse proxy에 위임하라 -**핵심요약:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead +**핵심요약:** Node는 gzip이나 SSL Termination과 같이 CPU 집약적인 작업에 약하다. 이런게 필요할 경우 '실제' 미들웨어 서비스인 nginx, HAproxy 혹은 클라우드 서비스를 사용해야한다 -**그렇게 하지 않을 경우:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly +**그렇게 하지 않을 경우:** 불쌍한 싱글 스레드는 어플리케이션의 코어를 처리하는 대신 인프라 작업을 수행하는 것에 더 바쁘게되고 성능은 저하될 것이다 -🔗 [**자세히 보기: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md) +🔗 [**자세히 보기: 가능한 모든 것들(예: gzip, SSL)을 reverse proxy에 위임하라**](/sections/production/delegatetoproxy.md)

-## ![✔] 5.4. Lock dependencies +## ![✔] 5.4. 의존성을 잠궈라(Lock dependencies) -**핵심요약:** Your code must be identical across all environments, but amazingly npm lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using npm config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grained control use `npm shrinkwrap`. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default +**핵심요약:** 코드는 모든 환경에서 동일해야하지만 놀랍게도 npm을 사용하면 기본적으로 환경간에 종속성이 달라질 수 있다. 다양한 환경에서 패키지를 설치하면 패키지의 최신 패치 버전을 가져온다. npm config 파일 인 .npmrc를 사용하여이 문제를 극복하라. 각 환경에 각 패키지의 최신 버전이 아닌 정확한 버전을 저장하도록 알려준다. 또는 세밀하게 제어하려면`npm shrinkwrap`을 사용하라. \* 업데이트 : NPM5 현재 기본적으로 종속성이 잠겨 있다. 새로운 패키지 관리자인 Yarn도 기본적으로 잠겨 있다. -**그렇게 하지 않을 경우:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**그렇게 하지 않을 경우:** QA팀은 코드를 철저히 테스트했지만 테스트 환경과는 다르게 작동하는 버전을 승인할 것이다. 심지어 더 나쁜 것은 같은 프로덕션 클러스터의 여러 서버가 서로 다른 코드를 실행할 수도 있다는 것이다. 🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.md)

-## ![✔] 5.5. Guard process uptime using the right tool +## ![✔] 5.5. 올바른 도구를 이용해 프로세스가 가동되는 시간을 보호하라 -**핵심요약:** The process must go on and get restarted upon failures. For simple scenarios, process management tools like PM2 might be enough but in today's ‘dockerized’ world, cluster management tools should be considered as well +**핵심요약:** 프로세스는 계속 진행되어야하며 실패시 다시 시작해야한다. 간단한 시나리오의 경우 PM2와 같은 프로세스 관리 도구로도 충분하지만 오늘날 '도커가 사용되는' 세계에서는 클러스터 관리 도구도 고려해야한다 -**그렇게 하지 않을 경우:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos +**그렇게 하지 않을 경우:** 명확한 전략없이 수십 개의 인스턴스와 너무 많은 도구 (클러스터 관리, 도커, PM2)를 실행하면 개발자가 혼란을 겪을 수 있다 🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.md)

-## ![✔] 5.6. Utilize all CPU cores +## ![✔] 5.6. 모든 CPU를 활용하라 -**핵심요약:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) +**핵심요약:** 기본적으로 Node 어플리케이션은 하나의 CPU 코어에서 실행되고 다른 CPU는 동작하지 않는다. 노드 프로세스를 복제하여 모든 CPU를 활용하는 것은 당신의 몫이다. 중소형 어플리케이션의 경우 노드 클러스터 또는 PM2를 사용하면 된다. 더 큰 앱의 경우 Docker 클러스터(예: K8S, ECS) 또는 Linux 초기화 시스템(예: systemd)을 기반으로하는 배포 스크립트를 사용하여 프로세스를 복제하는 것이 좋다 -**그렇게 하지 않을 경우:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) +**그렇게 하지 않을 경우:** 사용 가능한 리소스의 25%, 혹은 훨씬 적게 활용할 것이다. 일반적인 서버는 CPU 코어가 4개 이상인 점을 감안할 때, Node를 아무 생각없이 배포하게 되면 그 중 단 1 개만 사용하게 될것이다. AWS beanstalk와 같은 PaaS 서비스 사용하더라도 말이다 -🔗 [**자세히 보기: Utilize all CPU cores**](/sections/production/utilizecpu.md) +🔗 [**자세히 보기: 모든 CPU를 활용하라**](/sections/production/utilizecpu.md)

-## ![✔] 5.7. Create a ‘maintenance endpoint’ +## ![✔] 5.7. ‘유지 관리용 엔드 포인트’를 따로 만들어라 -**핵심요약:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tests tools, some valuable information and operations are easier done using code +**핵심요약:** 보안적으로 안전한 API에서 메모리 사용 및 REPL 등과 같은 시스템 관련 정보를 노출하라. 표준 및 실전 테스트(battle-tests) 도구를 사용하는 것이 좋기는 하지만 몇몇 유용한 정보와 작업은 코드를 통해 쉽게 수행 할 수 있다 -**그렇게 하지 않을 경우:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes +**그렇게 하지 않을 경우:** 단지 서버 진단을 목적으로 일부 정보를 추출하기 위하여 상용서버에 코드를 배포하는 "진단용 배포"를 자주 수행하고 있게 될 것이다 🔗 [**자세히 보기: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md)

-## ![✔] 5.8. Discover errors and downtime using APM products +## ![✔] 5.8. APM을 사용하여 오류 및 가동 중지 시간을 발견하라 -**핵심요약:** Application monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause +**핵심요약:** 어플리케이션 성능 관리 제품(Application Performance Management, APM)은 코드베이스 및 API를 사전에 측정하여 기존 모니터링 시스템을 뛰어 넘어 서비스 및 계층 전반에서 사용자 경험을 측정한다. 예를 들어 일부 APM 제품은 최종 사용자 측면에서 느리게 로드되는 트랜잭션을 강조 표시 하면서 근본 원인을 제시할 수 있다 -**그렇게 하지 않을 경우:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX +**그렇게 하지 않을 경우:** API 성능 및 가동 중지 시간을 측정하는 데 많은 노력을 기울이게 될 수 있다. 실제 상황에서 가장 느린 코드가 무엇인지, 그리고 이것이 UX에 미치는 영향을 알지 못할 것이다 🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.md)

-## ![✔] 5.9. Make your code production-ready +## ![✔] 5.9. 당신의 코드는 언제든 상용에 배포될수 있다 -**핵심요약:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) +**핵심요약:** 첫날부터 배포를 염두에두고 코드를 작성하라. 다소 모호하게 들릴것 같아서 상용 유지 보수와 밀접하게 관련된 몇 가지 개발 팁을 컴파일해 두었다 (아래의 gist를 클릭하라) -**그렇게 하지 않을 경우:** A world champion IT/DevOps guy won’t save a system that is badly written +**그렇게 하지 않을 경우:** 세계 최고의 IT/DevOps 전문가도 잘못 작성된 코드로 이루어진 시스템은 구하지 못한다 🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productioncode.md)

-## ![✔] 5.10. Measure and guard the memory usage +## ![✔] 5.10. 메모리 사용량 측정 및 보호 -**핵심요약:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system +**핵심요약:** Node.js는 메모리와 관련하여 논란의 여지가 있다. v8 엔진은 메모리 사용량 (1.4GB)에 대한 제한이 있으며 노드의 코드에서 메모리 누수가 발생하는 알려진 방법이 존재하므로 노드의 프로세스 메모리를 관찰하는 것이 필수적이다. 작은 응용 프로그램에서는 Shell 명령을 사용하여 주기적으로 메모리를 측정 할 수 있지만 중대형 어플리케이션에서는 강력한 모니터링 시스템을 통해 메모리를 감시하는 것을 고려하라 -**그렇게 하지 않을 경우:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) +**그렇게 하지 않을 경우:** [월마트](https://www.joyent.com/blog/walmart-node-js-memory-leak)에서 일어났던 것처럼 메모리가 하루에 수백 메가씩 누수 될 수 있다 -🔗 [**자세히 보기: Measure and guard the memory usage**](/sections/production/measurememory.md) +🔗 [**자세히 보기: 메모리 사용량 측정 및 보호**](/sections/production/measurememory.md)

@@ -1193,751 +1193,3 @@ We appreciate any contribution, from a single word fix to a new best practice. B ⭐ [Ryan Ouyang](https://github.com/ryanouyang)


-======= -[✔]: assets/images/checkbox-small-blue.png - -# Node.js 모범 사례 - -

- Node.js Best Practices -

- -
- -
- 53 items Last update: Apr 23, 2018 Updated for Node v.8.11 -
- -
- -[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **트위터에서 팔로우 하세요!** [**@nodepractices**](https://twitter.com/nodepractices/) - -
- -다른 언어로 읽기: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR**, ![RU](/assets/flags/RU.png)**RU**, ![TR](/assets/flags/TR.png)**TR** 는 작업중입니다!)](#translations) - -
- -# 안녕하세요! 먼저 알아야 할 3가지가 있습니다: - -**1. 이 문서를 읽는 것은, 사실상 수십 개의 베스트 Node.js 문서를 읽는 것입니다. -** 이 문서는 Node.js 의 가장 인기 있는 모범사례(Best Practice)들을 모은 요약집 및 큐레이션입니다. - -**2. 가장 큰 모음집이며, 매주 성장하고 있습니다. -** 현재, 50개 이상의 모범사례들과, 스타일 가이드, 아키텍처적인 팁들이 제공되고 있습니다. 이 문서의 업데이트를 위해 새로운 이슈들과 PR들이 매일 만들어지고 있습니다. 우리는 이 문서의 잘못된 코드를 고치거나 새로운 아이디어들을 제안하는 것을 매우 환영합니다. [마일스톤 보러가기](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) - -**3. 항목 대부분은 추가적인 정보가 있습니다 -** 항목 옆쪽에 존재하는 **🔗자세히 보기** 링크에서 코드 예제, 참조 블로그 또는 기타 정보들을 확인 할 수 있습니다. - -


- -## 목차 - -1. [프로젝트 구조 설계 (5)](#1-프로젝트-구조-설계) -2. [에러 처리 방법 (11)](#2-에러-처리-방법) -3. [코드 스타일 (12) ](#3-코드-스타일) -4. [테스트 및 전체 품질 관리 (8) ](#4-테스트-및-전체-품질-관리) -5. [운영 환경으로 전환하기 (16) ](#5-운영-환경으로-전환하기) -6. 보안 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) -7. 성능 ([예정](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) - -


- -# `1. 프로젝트 구조 설계` - -## ![✔] 1.1 컴포넌트 기반으로 설계하라 - -**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 최악의 함정은 많은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 하나로 통째로 짜여진 코드는 개발자가 새로운 기능들을 협업하는 속도를 느려지게 한다. 그 대신에 당신의 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 가지게 하고 컴포넌트의 각 단위가 작고 간단하게 유지되도록 하라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인하라. - -**그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 미치는 영향을 깨닫기위해 몸부림치거나 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 할때 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다. - -🔗 [**자세히 보기: 컴포넌트로 구조화하기**](/sections/projectstructre/breakintcomponents.korean.md) - -

- -## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라. - -**핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려를 깨끗하게 분리할 뿐만 아니라 모의 객체를 만들거나(mocking) 테스트하기가 굉장히 쉽게 만든다. 이것이 굉장히 일반적인 패턴임에도, API 개발자는 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 그렇게 하는것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다. - -**그렇게 하지 않을 경우:** 웹 객체를 다른 계층과 뒤섞은 앱은 테스트 코드, CRON 작업이나 Express가 아닌 다른 곳에서 접근이 불가능하게 한다. - -🔗 [**자세히 보기: 앱을 계층화하기**](/sections/projectstructre/createlayers.korean.md) - -

- -## ![✔] 1.3 유틸리티들을 NPM 패키지로 감싸라(wrap) - -**핵심요약:** 커다란 코드 기반으로 구성되어있는 커다란 앱에서는 로깅, 암호화 같은 횡단 관심사(cross-cutting-concern)가 존재하는 유틸의 경우 당신 자신의 코드로 감싸져야하며 개인 NPM package로 노출이 되어야한다. 이것은 여러 코드 기반과 프로젝트들 사이에서 그것들을 공유가 가능하도록 해준다. - -**그렇게 하지 않을 경우:** 당신 자신만의 배포 및 의존성 바퀴(wheel)를 새로 발명해야 할 것이다. - -🔗 [**자세히 보기: 기능으로 구조화 하기**](/sections/projectstructre/wraputilities.korean.md) - -

- -## ![✔] 1.4 Express의 app과 server를 분리하라 - -**핵심요약:** 'Express' 정의를 적어도 API 선언(app.js)과 네트워크 부분(WWW)의 두 개 파일로 나눠서 전체 [Express](https://expressjs.com/)앱을 하나의 큰 파일에 정의하는 불쾌한 습관을 피해라. 더 좋은 구조는 API 선언을 컴포넌트에 위치시키는 것이다. - -**그렇게 하지 않을 경우:** API는 HTTP 요청으로만 테스트가 가능 할것이다(커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것이 크게 즐겁지는 않을 것이다. - -🔗 [**자세히 보기: Express를 'app'과 'server'로 분리하기**](/sections/projectstructre/separateexpress.korean.md) - -

- -## ![✔] 1.5 환경을 인식하는, 보안적인, 계층적인 설정을 사용하라 - -**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경 변수에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 바깥에서 관리되어야하고 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. - -**그렇게 하지 않을 경우:** 위의 구성 요구사항 중 어느 것도 만족시키지 못한다면 개발팀 혹은 데브옵스팀을 늪으로 몰아갈 수 있다. 아마도 두 팀 모두일 것이다. - -🔗 [**자세히 보기: 구성 모범 사례**](/sections/projectstructre/configguide.korean.md) - -


- -

⬆ 목차로 돌아가기

- -# `2. 에러 처리 방법` - -## ![✔] 2.1 비동기 에러 처리시에는 Async-Await 혹은 Promise를 사용하라 - -**핵심요약:** 비동기 에러를 콜백 스타일로 처리하는 것은 지옥으로 가는 급행열차일 것이다(운명의 피라미드로 잘 알려진). 당신이 코드에 줄 수 있는 가장 큰 선물은 평판이 좋은 Promise 라이브러리를 사용하거나 훨신 작고 친숙한 코드 문법인 try-catch를 사용하게 해주는 Async-Await를 사용하는 것이다. - -**그렇게 하지 않을 경우:** Node.js 콜백 스타일인 function(err, response)는 에러 처리와 일반 코드의 혼합, 코드의 과도한 중첩, 이상한 코딩 패턴 때문에 유지보수가 불가능한 코드로가는 확실한 길이다. - -🔗 [**자세히 보기: 콜백 피하기**](/sections/errorhandling/asyncerrorhandling.korean.md) - -

- -## ![✔] 2.2 내장된 Error 객체만 사용하라 - -**핵심요약:** 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던진다(throw). 이것은 에러처리 로직과 모듈 사이의 상호운영성을 복잡하게 한다. 당신이 Promise를 거부(reject)하든, 예외를 던지든, 에러를 냈건 내장된 Error 객체를 이용하는 것은 균일성을 향상하고 정보의 손실을 방지하게 만들것이다. - -**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워 질것이다. 더 나쁜 것은, 사용자가 정의한 타입으로 에러를 나타내는 것은 스택 정보(stack trace)와 같은 중요한 에러 정보를 손실할 가능성이 있다는 것이다! - -🔗 [**자세히 보기: 내장된 Error 객체 사용하기**](/sections/errorhandling/useonlythebuiltinerror.korean.md) - -

- -## ![✔] 2.3 동작상의 에러와 프로그래머 에러를 구분하라 - -**핵심요약:** API에서 잘못된 입력을 받는 것과 같은 동작상의 에러는 에러의 영향을 완전히 이해할수 있고 신중하게 처리 할수있는 알려진 경우를 의미한다. 반면에 정의되지 않은 변수를 읽는 것과 같은 프로그래머 에러는 어플리케이션을 우아하게 다시 시작하도록 만드는 알수 없는 코드 에러를 의미한다. - -**그렇게 하지 않을 경우:** 당신은 에러가 날때마다 어플리케이션을 다시 시작할수도 있다. 하지만 왜 사소하고 예측가능한 동작상의 오류때문에 5000명의 온라인 사용자를 다운시키는 것인가? 나머지 상황 또한 이상적이지 않다. 알수없는 이슈(프로그래머 에러)가 났는데 어플리케이션을 그대로 두는 것은 예측이 불가능한 동작을 일으킬 수 있다. 두 가지를 구별하는 것은 현명한 행동과 주어진 상황에 따른 균형잡힌 접근을 가능하게 한다. - -🔗 [**자세히 보기: 동작상의 에러와 프로그래머 에러**](/sections/errorhandling/operationalvsprogrammererror.korean.md) - -

- -## ![✔] 2.4 에러를 Express 미들웨어로 처리하지 말고 중앙집중적으로 처리하라 - -**핵심요약:** 관리자에게 메일을 보내거나 로깅을 하는 것과 같은 에러 처리는 에러가 발생할 때 모든 엔드포인트(예를 들어 Express 미들웨어, cron 작업, 단위 테스트 등)가 호출하는 에러전용 중앙집중 객체로 캡슐화 되어야한다. - -**그렇게 하지 않을 경우:** 한 곳에서 에러를 처리하지 않는 것은 코드 중복과 부적절한 에러처리로 이어진다. - -🔗 [**자세히 보기: 중앙집중적으로 에러 처리하기**](/sections/errorhandling/centralizedhandling.korean.md) - -

- -## ![✔] 2.5 Swagger를 이용해 API 에러를 문서화하라 - -**핵심요약:** API를 호출한 사람들이 어떤 에러가 반환 될수 있는지 알게하여 충돌없이 신중하게 처리 할 수 있도록하라. 이것은 보통 Swagger와 같은 API 문서화 프레임워크를 통해 이루어진다. - -**그렇게 하지 않을 경우:** API 클라이언트는 알수 없는 에러로 인해 충돌 후에 재시작을 결정할수도 있을 것이다. 참고: 당신의 API를 호출한 사람이 당신 자신 일수도 있다.(마이크로서비스 환경에서는 아주 일반적임). - -🔗 [**자세히 보기: Swagger에서 에러 문서화하기**](/sections/errorhandling/documentingusingswagger.korean.md) - -

- -## ![✔] 2.6 이상한 것이 들어왔을때 프로세스를 정상적으로 중단하라 - -**핵심요약:** 알수 없는 에러(프로그래머 에러, 모범사례 #3번 참조)가 발생하면 어플리케이션의 건강상태에 대한 불확실성이 있다. 일반적인 방법은 Forever와 PM2 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. - -**그렇게 하지 않을 경우:** 익숙치 않은 예외가 발생하면 일부 객체가 오류 상태(예를 들어 전역적으로 사용되지만 내부 오류로 인해 이벤트를 더이상 발생시키지 않는 Event Emitter)일 수 있으며 향후의 모든 요청이 실패하거나 미친것처럼(crazily) 동작할 수 있다. - -🔗 [**자세히 보기: 프로세스 중단하기**](/sections/errorhandling/shuttingtheprocess.korean.md) - -

- -## ![✔] 2.7 Use a mature logger to increase error visibility - -**TL;DR:** A set of mature logging tools like Winston, Bunyan or Log4J, will speed-up error discovery and understanding. So forget about console.log - -**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late - -🔗 [**자세히 보기: using a mature logger**](/sections/errorhandling/usematurelogger.korean.md) - -

- -## ![✔] 2.8 Test error flows using your favorite test framework - -**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenario but also handle and return the right errors. Testing frameworks like Mocha & Chai can handle this easily (see code examples within the "Gist popup") - -**Otherwise:** Without testing, whether automatically or manually, you can’t rely on our code to return the right errors. Without meaningful errors – there’s no error handling - -🔗 [**자세히 보기: testing error flows**](/sections/errorhandling/testingerrorflows.korean.md) - -

- -## ![✔] 2.9 Discover errors and downtime using APM products - -**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes and slow parts that you were missing - -**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX - -🔗 [**자세히 보기: using APM products**](/sections/errorhandling/apmproducts.korean.md) - -

- -## ![✔] 2.10 Catch unhandled promise rejections - -**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle. Even if your code is subscribed to process.uncaughtException! Overcome this by registering to the event process.unhandledRejection - -**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about - -🔗 [**자세히 보기: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.korean.md) - -

- -## ![✔] 2.11 Fail fast, validate arguments using a dedicated library - -**TL;DR:** This should be part of your Express best practices – Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like Joi - -**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? - -🔗 [**자세히 보기: failing fast**](/sections/errorhandling/failfast.korean.md) - -


- -

⬆ 목차로 돌아가기

- -# `3. 코드 스타일` - -## ![✔] 3.1 Use ESLint - -**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint - -**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking about the project's code style - -

- -## ![✔] 3.2 Node.js Specific Plugins - -**TL;DR:** On top of ESLint standard rules that cover vanilla JS only, add Node-specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) - -**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early - -

- -## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line - -**TL;DR:** The opening curly braces of a code block should be in the same line of the opening statement - -### Code Example - -```javascript -// Do -function someFunction() { - // code block -} - -// Avoid -function someFunction() -{ - // code block -} -``` - -**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: - -🔗 [**자세히 보기:** "Why does a results vary based on curly brace placement?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) - -

- -## ![✔] 3.4 Don't Forget the Semicolon - -**TL;DR:** While not unanimously agreed upon, it is still recommended to put a semicolon at the end of each statement. This will make your code more readable and explicit to other developers who read it - -**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one which might lead to some undesired results - -

- -## ![✔] 3.5 Name Your Functions - -**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot - -**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions - -

- -## ![✔] 3.6 Naming conventions for variables, constants, functions and classes - -**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions and **_UpperCamelCase_** (capital first letter as well) when naming classes. This will help you to easily distinguish between plain variables/functions, and classes that require instantiation. Use descriptive names, but try to keep them short - -**Otherwise:** Javascript is the only language in the world which allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase - -### Code Example - -```javascript -// for class name we use UpperCamelCase -class SomeClassExample {} - -// for const names we use the const keyword and lowerCamelCase -const config = { - key: 'value' -}; - -// for variables and functions names we use lowerCamelCase -let someVariableExample = 'value'; -function doSomething() {} -``` - -

- -## ![✔] 3.7 Prefer const over let. Ditch the var - -**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring const will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have const and let at your disposal - -**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes - -🔗 [**자세히 보기: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) - -

- -## ![✔] 3.8 Requires come first, and not inside functions - -**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems - -**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its own dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function - -

- -## ![✔] 3.9 Do Require on the folders, not directly on the files - -**TL;DR:** When developing a module/library in a folder, place an index.js file that exposes the module's -internals so every consumer will pass through it. This serves as an 'interface' to your module and eases -future changes without breaking the contract - -**Otherwise:** Changing the internal structure of files or the signature may break the interface with -clients - -### Code example - -```javascript -// Do -module.exports.SMSProvider = require('./SMSProvider'); -module.exports.SMSNumberResolver = require('./SMSNumberResolver'); - -// Avoid -module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js'); -module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js'); -``` - -

- -## ![✔] 3.10 Use the `===` operator - -**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal - -**Otherwise:** Unequal variables might return true when compared with the `==` operator - -### Code example - -```javascript -'' == '0' // false -0 == '' // true -0 == '0' // true - -false == 'false' // false -false == '0' // true - -false == undefined // false -false == null // false -null == undefined // true - -' \t\r\n ' == 0 // true -``` - -All statements above will return false if used with `===` - -

- -## ![✔] 3.11 Use Async Await, avoid callbacks - -**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch - -**Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and make it difficult to reason about the code flow - -🔗[**자세히 보기:** Guide to async await 1.0](https://github.com/yortus/asyncawait) - -

- -## ![✔] 3.12 Use Fat (=>) Arrow Functions - -**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older API that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. 'this') - -**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read - -🔗 [**Read mode: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) - -


- -

⬆ 목차로 돌아가기

- -# `4. 테스트 및 전체 품질 관리` - -## ![✔] 4.1 At the very least, write API (component) testing - -**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' run out of control and being abandoned. For that reason, prioritize and start with API testing which is the easiest to write and provide more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/). Afterward, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc - -**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage - -

- -## ![✔] 4.2 Detect code issues with a linter - -**TL;DR:** Use a code linter to check basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices) on Code Style Practices - -**Otherwise:** You may let pass some anti-pattern and possible vulnerable code to your production environment. - -

- -## ![✔] 4.3 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) - -**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of complex setup that demands a steep learning curve. Nowadays, it became much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully - -**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup - -🔗 [**자세히 보기: Choosing CI platform**](/sections/testingandquality/citools.korean.md) - -

- -## ![✔] 4.4 Constantly inspect for vulnerable dependencies - -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [nsp](https://github.com/nodesecurity/nsp) that can be invoked from your CI on every build - -**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious - -

- -## ![✔] 4.5 Tag your tests - -**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' - -**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests - -

- -## ![✔] 4.6 Check your test coverage, it helps to identify wrong test patterns - -**TL;DR:** Code coverage tools like [Istanbul/NYC ](https://github.com/gotwarlost/istanbul)are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold - -**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing - -

- -## ![✔] 4.7 Inspect for outdated packages - -**TL;DR:** Use your preferred tool (e.g. 'npm outdated' or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) to detect installed packages which are outdated, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version - -**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky - -

- -## ![✔] 4.8 Use docker-compose for e2e testing - -**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Docker-compose turns this problem into a breeze by crafting production-like environment using a simple text file and easy commands. It allows crafting all the dependent services, DB and isolated network for e2e testing. Last but not least, it can keep a stateless environment that is invoked before each test suite and dies right after - -**Otherwise:** Without docker-compose teams must maintain a testing DB for each testing environment including developers machines, keep all those DBs in sync so test results won't vary across environments - -


- -

⬆ 목차로 돌아가기

- -# `5. 운영 환경으로 전환하기` - -## ![✔] 5.1. Monitoring! - -**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions - -**Otherwise:** Failure === disappointed customers. Simple - -🔗 [**자세히 보기: Monitoring!**](/sections/production/monitoring.korean.md) - -

- -## ![✔] 5.2. Increase transparency using smart logging - -**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted - -**Otherwise:** You end-up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information - -🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.korean.md) - -

- -## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy - -**TL;DR:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead - -**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly - -🔗 [**자세히 보기: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.korean.md) - -

- -## ![✔] 5.4. Lock dependencies - -**TL;DR:** Your code must be identical across all environments, but amazingly NPM lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using NPM config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grain control use NPM” shrinkwrap”. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default - -**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently at production. Even worse, different servers at the same production cluster might run different code - -🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.korean.md) - -

- -## ![✔] 5.5. Guard process uptime using the right tool - -**TL;DR:** The process must go on and get restarted upon failures. For simple scenarios, ‘restarter’ tools like PM2 might be enough but in today ‘dockerized’ world – a cluster management tools should be considered as well - -**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to a DevOps chaos - -🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.korean.md) - -

- -## ![✔] 5.6. Utilize all CPU cores - -**TL;DR:** At its basic form, a Node app runs on a single CPU core while all other are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) - -**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) - -🔗 [**자세히 보기: Utilize all CPU cores**](/sections/production/utilizecpu.korean.md) - -

- -## ![✔] 5.7. Create a ‘maintenance endpoint’ - -**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tests tools, some valuable information and operations are easier done using code - -**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes - -🔗 [**자세히 보기: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.korean.md) - -

- -## ![✔] 5.8. Discover errors and downtime using APM products - -**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause - -**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affects the UX - -🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.korean.md) - -

- -## ![✔] 5.9. Make your code production-ready - -**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) - -**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written - -🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productoncode.korean.md) - -

- -## ![✔] 5.10. Measure and guard the memory usage - -**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leaks memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large app consider baking your memory watch into a robust monitoring system - -**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) - -🔗 [**자세히 보기: Measure and guard the memory usage**](/sections/production/measurememory.korean.md) - -

- -## ![✔] 5.11. Get your frontend assets out of Node - -**TL;DR:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single threaded model - -**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content - -🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.korean.md) - -

- -## ![✔] 5.12. Be stateless, kill your Servers almost every day - -**TL;DR:** Store any type of data (e.g. users session, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior - -**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server - -🔗 [**자세히 보기: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.korean.md) - -

- -## ![✔] 5.13. Use tools that automatically detect vulnerabilities - -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can get easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately - -**Otherwise:** Otherwise: Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious - -🔗 [**자세히 보기: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.korean.md) - -

- -## ![✔] 5.14. Assign ‘TransactionId’ to each log statement - -**TL;DR:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside - -**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue - -🔗 [**자세히 보기: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.korean.md) - -

- -## ![✔] 5.15. Set NODE_ENV=production - -**TL;DR:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many NPM packages determining the current environment and optimize their code for production - -**Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes the slower by a factor of three! - -🔗 [**자세히 보기: Set NODE_ENV=production**](/sections/production/setnodeenv.korean.md) - -

- -## ![✔] 5.16. Design automated, atomic and zero-downtime deployments - -**TL;DR:** Researches show that teams who perform many deployments – lowers the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improves the deployment process. You should probably achieve that using Docker combined with CI tools as they became the industry standard for streamlined deployment - -**Otherwise:** Long deployments -> production down time & human-related error -> team unconfident and in making deployment -> less deployments and features - -

- -## ![✔] 5.17. Use an LTS release of Node.js - -**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements - -**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain - -🔗 [**자세히 보기: Use an LTS release of Node.js**](/sections/production/LTSrelease.korean.md) - -


- -

⬆ 목차로 돌아가기

- -# `보안` - -## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까? - -


- -# `성능` - -## 컨트리뷰터들이 현재 작업중 입니다. 함께 하시겠습니까? - -


- -# 마일스톤 - -이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. 만약 이 프로젝트에 기여를 하고 싶으시면 [마일스톤](https://github.com/i0natan/nodebestpractices/milestones) 을 보고 참여하십시오. - -

- -## 번역 - -모든 번역은 커뮤니티에 의해 기여되고 있습니다. 이미 완성된 번역이나, 진행중, 새로운 번역에 대한 도움은 언제나 환영합니다! - -### 번역 작업 완료 - -* ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) - -### 번역 작업중 - -* ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -* ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -* ![KR](/assets/flags/KR.png) [Korean](https://github.com/i0natan/nodebestpractices/blob/korean-translation/README.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -* ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) -* ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -* ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) - -


- -# Contributors - -## `Yoni Goldberg` - -Independent Node.js consultant who works with customers in USA, Europe, and Israel on building large-scale scalable Node applications. Many of the best practices above were first published in his blog post at [http://www.goldbergyoni.com](http://www.goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com - -## `Ido Richter` - -👨‍💻 Software engineer, 🌐 web developer, 🤖 emojis enthusiast - -## `Refael Ackermann` [@refack](https://github.com/refack) <refack@gmail.com> (he/him) - -Node.js Core Collaborator, been noding since 0.4, and have noded in multiple production sites. Founded `node4good` home of [`lodash-contrib`](https://github.com/node4good/lodash-contrib), [`formage`](https://github.com/node4good/formage), and [`asynctrace`](https://github.com/node4good/asynctrace). -`refack` on freenode, Twitter, GitHub, GMail, and many other platforms. DMs are open, happy to help - -## `Bruno Scheufler` - -💻 full-stack web developer and Node.js enthusiast - -## `Kyle Martin` [@js-kyle](https://github.com/js-kyle) -Full Stack Developer based in New Zealand, interested in architecting and building Node.js applications to perform at global scale. Keen contributor to open source software, including Node.js Core. - -


- -# Thank You Notes - -This repository is being kept up to date thanks to the help from the community. We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A 🌻 marks a successful pull request and a ⭐ marks an approved new best practice - -### Flowers
- -🌻 [Kevin Rambaud](https://github.com/kevinrambaud), -🌻 [Michael Fine](https://github.com/mfine15), -🌻 [Shreya Dahal](https://github.com/squgeim), -🌻 [ChangJoo Park](https://github.com/ChangJoo-Park), -🌻 [Matheus Cruz Rocha](https://github.com/matheusrocha89), -🌻 [Yog Mehta](https://github.com/BitYog), -🌻 [Kudakwashe Paradzayi](https://github.com/kudapara), -🌻 [t1st3](https://github.com/t1st3), -🌻 [mulijordan1976](https://github.com/mulijordan1976), -🌻 [Matan Kushner](https://github.com/matchai), -🌻 [Fabio Hiroki](https://github.com/fabiothiroki), -🌻 [James Sumners](https://github.com/jsumners), -🌻 [Chandan Rai](https://github.com/crowchirp), -🌻 [Dan Gamble](https://github.com/dan-gamble), -🌻 [PJ Trainor](https://github.com/trainorpj), -🌻 [Remek Ambroziak](https://github.com/reod), -🌻 [Yoni Jah](https://github.com/yonjah), -🌻 [Misha Khokhlov](https://github.com/hazolsky), -🌻 [Evgeny Orekhov](https://github.com/EvgenyOrekhov), -🌻 [Gediminas Petrikas](https://github.com/gediminasml), -🌻 [Isaac Halvorson](https://github.com/hisaac), -🌻 [Vedran Karačić](https://github.com/vkaracic), -🌻 [lallenlowe](https://github.com/lallenlowe), -🌻 [Nathan Wells](https://github.com/nwwells), -🌻 [Paulo Vítor S Reis](https://github.com/paulovitin), -🌻 [syzer](https://github.com/syzer), -🌻 [David Sancho](https://github.com/davesnx), -🌻 [Robert Manolea](https://github.com/pupix), -🌻 [Xavier Ho](https://github.com/spaxe), -🌻 [Aaron Arney](https://github.com/ocularrhythm), -🌻 [Jan Charles Maghirang Adona](https://github.com/septa97), -🌻 [Allen Fang](https://github.com/AllenFang), -🌻 [Leonardo Villela](https://github.com/leonardovillela), -🌻 [Michal Zalecki](https://github.com/MichalZalecki) -🌻 [Chris Nicola](https://github.com/chrisnicola), -🌻 [Alejandro Corredor](https://github.com/aecorredor), -🌻 [Ye Min Htut](https://github.com/ymhtut), -🌻 [cwar](https://github.com/cwar), -🌻 [Yuwei](https://github.com/keyfoxth), -🌻 [Utkarsh Bhatt](https://github.com/utkarshbhatt12) -🌻 [Duarte Mendes](https://github.com/duartemendes) -🌻 [Sagir Khan](https://github.com/sagirk) -🌻 [Jason Kim](https://github.com/serv) - - -### Stars
- -⭐ [Kyle Martin](https://github.com/js-kyle) - -


->>>>>>> korean-translation From 540d395aa28128253118b306355fb5e4fb8d8c0e Mon Sep 17 00:00:00 2001 From: Sangbeom Han Date: Tue, 16 Jul 2019 21:41:33 +0900 Subject: [PATCH 0016/1795] Translate Section 5 into Korean Fix some typo --- README.korean.md | 64 ++++++++++++++++++++---------------------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/README.korean.md b/README.korean.md index d48632abe..ea12b6622 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1,4 +1,3 @@ -<<<<<<< HEAD [✔]: assets/images/checkbox-small-blue.png # Node.js 모범 사례 @@ -495,7 +494,7 @@ null == undefined // true


-

⬆ Return to top

+

⬆ 목차로 돌아가기

# `5. 운영환경으로 전환하기` @@ -593,91 +592,91 @@ null == undefined // true **핵심요약:** Node.js는 메모리와 관련하여 논란의 여지가 있다. v8 엔진은 메모리 사용량 (1.4GB)에 대한 제한이 있으며 노드의 코드에서 메모리 누수가 발생하는 알려진 방법이 존재하므로 노드의 프로세스 메모리를 관찰하는 것이 필수적이다. 작은 응용 프로그램에서는 Shell 명령을 사용하여 주기적으로 메모리를 측정 할 수 있지만 중대형 어플리케이션에서는 강력한 모니터링 시스템을 통해 메모리를 감시하는 것을 고려하라 -**그렇게 하지 않을 경우:** [월마트](https://www.joyent.com/blog/walmart-node-js-memory-leak)에서 일어났던 것처럼 메모리가 하루에 수백 메가씩 누수 될 수 있다 +**그렇게 하지 않을 경우:** [월마트](https://www.joyent.com/blog/walmart-node-js-memory-leak)에서 일어났던 것처럼 메모리가 하루에 수백 MB씩 누수 될 수 있다 🔗 [**자세히 보기: 메모리 사용량 측정 및 보호**](/sections/production/measurememory.md)

-## ![✔] 5.11. Get your frontend assets out of Node +## ![✔] 5.11. Node.js에서 프론트 엔드 파일 가져오기 -**핵심요약:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single-threaded model +**핵심요약:** 단일 스레드 모델로 인해 정적 파일을 많이 처리 할 때 Node.js 성능이 실제로 손상되기 때문에 전용 미들웨어(nginx, S3, CDN 등)를 사용하여 프론트 엔드 컨텐츠를 제공하는게 좋다 -**그렇게 하지 않을 경우:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content +**그렇게 하지 않을 경우:** 단일 노드 스레드는 동적 컨텐츠를 전달하는 작업에 리소스를 할당하는 대신 수백 개의 html/images/angular/react 파일을 스트리밍 하느라 분주할 것이다 🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.md)

-## ![✔] 5.12. Be stateless, kill your servers almost every day +## ![✔] 5.12. 무상태(stateless)로 운영하고, 거의 매일 서버를 재부팅하라 -**핵심요약:** Store any type of data (e.g. user sessions, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior +**핵심요약:** 어떤 유형의 데이터(예: 유저 세션, 캐시, 업로드된 파일)든 외부 데이터 저장소에 저장하라. 서버를 주기적으로 재부팅/교체하는 것을 고려하거나 명시적으로 무상태로 운영하게 만드는 Serverless 플랫폼(예: AWS Lambda)을 사용하는 것을 고려하라 -**그렇게 하지 않을 경우:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server +**그렇게 하지 않을 경우:** 해당서버에 오류가 발생하면 해당 서버만 제거하면 되는 것이 아니라 어플리케이션의 다운타임이 발생하게된다. 게다가 특정 서버에 의존하기 때문에 수평적 확장이 힘들어질 것이다 🔗 [**자세히 보기: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md)

-## ![✔] 5.13. Use tools that automatically detect vulnerabilities +## ![✔] 5.13. 취약점을 자동으로 탐지하는 도구 사용 -**핵심요약:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**핵심요약:** Express와 같은 가장 신뢰할만한 모듈조차도 시스템을 위험에 빠뜨릴 수있는 알려진 취약점이 존재한다. 이는 취약성을 지속적으로 확인하고(로컬 또는 GitHub에서) 경고하는 커뮤니티 및 상용 도구를 사용하여 쉽게 길들여질 수 있으며 일부는 즉시 패치 할 수도 있다 -**그렇게 하지 않을 경우:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious +**그렇게 하지 않을 경우:** 전용 도구없이 취약점으로부터 코드를 깨끗하게 유지하려면 새로운 취약점에 대한 데이터를 지속적으로 확인해야 할것이다 🔗 [**자세히 보기: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md)

-## ![✔] 5.14. Assign a transaction id to each log statement +## ![✔] 5.14. 로깅을 할때 트랜잭션 ID를 할당하라 -**핵심요약:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside +**핵심요약:** 하나의 요청 내에서 각 로그에 동일한 식별자(transaction-id: { some value })를 할당하라. 그렇게하면 로그를 분석할때 에러 전과 후에 어떤 일이 생겼는지 쉽게 알수있다. 비동기적인 특성때문에 Node.js에서 구현하기 쉽지는 않다. 아래의 예제를 확인하라 -**그렇게 하지 않을 경우:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue +**그렇게 하지 않을 경우:** 이전에 어떤일이 일어났는지에 대한 컨텍스트 없이 에러 로그를 확인하는 것은 문제를 해결하는 것을 더 어렵고 느리게 만든다 🔗 [**자세히 보기: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md)

-## ![✔] 5.15. Set NODE_ENV=production +## ![✔] 5.15. `NODE_ENV=production`로 설정하라 -**핵심요약:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many npm packages determine the current environment and optimize their code for production +**핵심요약:** 상용 최적화가 활성화 되어야하는지 아닌지를 표시하기 위해 `NODE_ENV`를 'production' 혹은 'development'로 설정하라. 많은 npm 패키지가 현재 환경을 확인하고 최적화한다 -**그렇게 하지 않을 경우:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! +**그렇게 하지 않을 경우:** 이 단순한 속성을 빠뜨리면 성능이 크게 저하된다. 예를 들어 Express에서 서버 사이드 렌더링(Server Side Rendering, SSP)을 사용할때 `NODE_ENV`를 빠뜨리면 3배 느려진다 🔗 [**자세히 보기: Set NODE_ENV=production**](/sections/production/setnodeenv.md)

-## ![✔] 5.16. Design automated, atomic and zero-downtime deployments +## ![✔] 5.16. 원자성의 자동화된 무중단 배포를 설계하라 -**핵심요약:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment +**핵심요약:** 연구 결과에 따르면 자주 배포를 하는 팀이 상용버전에서 심각한 에러가 발생할 가능성을 낮춘다고 한다. 위험이 따르는 수동적인 과정과 서비스의 중지시간이 필요하지 않은 빠르고 자동화된 배포는 배포 프로세스를 크게 향상시킨다. 간소화된 배포의 표준이 된 Docker와 CI 도구를 결합하여 이를 달성할 수 있다. -**그렇게 하지 않을 경우:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features +**그렇게 하지 않을 경우:** 오래걸리는 배포 작업 -> 상용버전 중지시간 및 사람에 의한 에러 -> 배포를 하는 것에 자신감이 없어진 팀 -> 더 적은 배포와 기능들

-## ![✔] 5.17. Use an LTS release of Node.js +## ![✔] 5.17. Node.js의 LTS 릴리즈 버전 사용 -**핵심요약:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements +**핵심요약:** LTS 버전의 Node.js를 사용하여 중요한 버그 수정, 보안 업데이트 및 성능 향상을 받아라 -**그렇게 하지 않을 경우:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain +**그렇게 하지 않을 경우:** 새로 발견된 버그나 취약점이 상용에서 운영중인 어플리케이션을 악용하는데 사용될 수 있으며, 다양한 모듈들에서 지원을 하지 않게 되고 유지보수하는 것이 힘들어 지게될것이다 🔗 [**자세히 보기: Use an LTS release of Node.js**](/sections/production/LTSrelease.md)

-## ![✔] 5.18. Don't route logs within the app +## ![✔] 5.18. 앱 내에서 로그를 라우팅하지 말라 -**핵심요약:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). +**핵심요약:** 로그의 목적지는 개발자가 어플리케이션 코드에 하드코딩해서는 안되고 프로그램이 실행되는 실행환경에서 정의되어야 한다. 개발자는 로거 유틸리티를 이용하여 로그를 `stdout`에 작성하고 상용환경(컨테이너, 서버 등)이 해당 `stdout`스트림을 적절한 목적지로 파이프해야한다 -**그렇게 하지 않을 경우:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns +**그렇게 하지 않을 경우:** 어플리케이션 로그 라우팅 처리 === 확장성 저하, 로그 유실, 관심사의 분리 실패(Separation of Concerns, SoC) 🔗 [**자세히 보기: Log Routing**](/sections/production/logrouting.md)


-

⬆ Return to top

+

⬆ 목차로 돌아가기

# `6. Security Best Practices` @@ -1013,21 +1012,12 @@ All translations are contributed by the community. We will be happy to get any h ### Translations in progress -<<<<<<< HEAD -* ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -* ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -* ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -* ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) -* ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -* ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) -======= - ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) - ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) - ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) - ![RU](/assets/flags/RU.png) [Russian](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/105)) - ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) - ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) ->>>>>>> master

From d1a894c00b205b9ca59e87916f579f9ae6c7985c Mon Sep 17 00:00:00 2001 From: Sangbeom Han Date: Tue, 16 Jul 2019 21:56:28 +0900 Subject: [PATCH 0017/1795] Translate Thank You Notes --- README.korean.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.korean.md b/README.korean.md index ea12b6622..97ddac651 100644 --- a/README.korean.md +++ b/README.korean.md @@ -995,22 +995,22 @@ null == undefined // true


-# Milestones +# 마일스톤 -To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project +이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. [마일스톤](https://github.com/i0natan/nodebestpractices/milestones)을 확인하시고 이 프로젝트에 기여하고 싶다면 작업중인 그룹에 참여하세요!
-## Translations +## 번역 -All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! +모든 번역은 커뮤니티에 의해 기여되고 있습니다. 이미 완성된 번역이나, 진행중, 새로운 번역에 대한 도움은 언제나 환영합니다! -### Completed translations +### 번역 작업 완료 - ![BR](/assets/flags/BR.png) [Brazilian Portuguese](/README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) - ![CN](/assets/flags/CN.png) [Chinese](README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) -### Translations in progress +### 번역 작업중 - ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) - ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) @@ -1083,9 +1083,9 @@ Our collaborators are members who are contributing to the repository on a reguar
-## Thank You Notes +## 감사 노트 -We appreciate any contribution, from a single word fix to a new best practice. Below is a list of everyone who contributed to this project. A 🌻 marks a successful pull request and a ⭐ marks an approved new best practice. +하나의 단어를 고치는 것 부터 새로운 모범사례까지, 어떤 컨트리뷰트든 환영합니다. 아래는 이 프로젝트에 컨트리뷰트한 모든 사람의 리스트입니다. 🌻는 성공적인 풀리퀘스트를 표시하고, ⭐는 새로운 모범사례를 의미합니다. ### Flowers From 12fbe782fa1fdbd80b29f70e6a9bc322d0f7a923 Mon Sep 17 00:00:00 2001 From: paritta Date: Sun, 11 Aug 2019 12:38:07 +0900 Subject: [PATCH 0018/1795] Translate 6.1 to korean --- README.korean.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.korean.md b/README.korean.md index 97ddac651..7d637650c 100644 --- a/README.korean.md +++ b/README.korean.md @@ -684,13 +684,13 @@ null == undefined // true 54 items -## ![✔] 6.1. Embrace linter security rules +## ![✔] 6.1. linter 보안 규칙 수용 -**핵심요약:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter +**핵심요약:** 이왕이면 코드를 작성하면서, 가능한한 빨리 eslint-plugin-security와 같은 보안 관련 linter 플러그인을 사용하여 보안 취약점을 잡으십시오. 이것은 eval, 자식 프로세스 호출, string iteral을 이용한 모듈 import (예를 들면 유저 인풋) 같은 보안 취약점을 잡는데 도움이 될 수 있다. 보안 linter가 잡는 코드를 보려면, 아래의 'Read more'을 클릭하십시오. -**그렇게 하지 않을 경우:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories +**그렇게 하지 않을 경우:** 개발 중에 직접 보안 취약점이 도리 수 있었던 것이 프로덕션에서 주요한 이슈가 된다. 또, 프로젝트가 일관된 보안 프렉티스를 따르지 않아, 취약점이 노출되거나 민감한 정보가 원격 저장소에 유출될 수 있다. 🔗 [**자세히 보기: Lint rules**](/sections/security/lintrules.md) From 93bdceaa9c637d97e6b1816125e81c84127ed70e Mon Sep 17 00:00:00 2001 From: forresst Date: Tue, 10 Dec 2019 13:51:28 +0100 Subject: [PATCH 0019/1795] Init french translation --- .operations/writing-guidelines.french.md | 31 + README.french.md | 1150 +++++++++++++++++ .../eslint_prettier.french.md | 26 + sections/errorhandling/apmproducts.french.md | 28 + .../asyncerrorhandling.french.md | 109 ++ .../catchunhandledpromiserejection.french.md | 87 ++ .../centralizedhandling.french.md | 164 +++ .../documentingusingswagger.french.md | 52 + sections/errorhandling/failfast.french.md | 67 + sections/errorhandling/monitoring.french.md | 17 + .../operationalvsprogrammererror.french.md | 85 ++ .../shuttingtheprocess.french.md | 99 ++ .../errorhandling/testingerrorflows.french.md | 81 ++ .../errorhandling/usematurelogger.french.md | 50 + .../useonlythebuiltinerror.french.md | 117 ++ sections/performance/block-loop.french.md | 50 + sections/performance/nativeoverutil.french.md | 70 + sections/production/LTSrelease.french.md | 20 + sections/production/apmproducts.french.md | 25 + .../production/assigntransactionid.french.md | 39 + sections/production/bestateless.french.md | 42 + .../createmaintenanceendpoint.french.md | 45 + sections/production/delegatetoproxy.french.md | 51 + .../detectvulnerabilities.french.md | 20 + sections/production/frontendout.french.md | 45 + sections/production/guardprocess.french.md | 17 + .../production/lockdependencies.french.md | 69 + sections/production/logrouting.french.md | 87 ++ sections/production/measurememory.french.md | 25 + sections/production/monitoring.french.md | 39 + sections/production/productioncode.french.md | 16 + sections/production/setnodeenv.french.md | 34 + sections/production/smartlogging.french.md | 40 + sections/production/utilizecpu.french.md | 26 + .../breakintcomponents.french.md | 37 + .../projectstructre/configguide.french.md | 43 + .../projectstructre/createlayers.french.md | 13 + .../projectstructre/separateexpress.french.md | 98 ++ .../projectstructre/thincomponents.french.md | 27 + .../projectstructre/wraputilities.french.md | 13 + .../avoid_publishing_secrets.french.md | 44 + sections/security/avoideval.french.md | 23 + sections/security/bcryptpasswords.french.md | 32 + sections/security/childprocesses.french.md | 31 + .../commonsecuritybestpractices.french.md | 99 ++ .../security/dependencysecurity.french.md | 53 + sections/security/escape-output.french.md | 62 + sections/security/expirejwt.french.md | 44 + sections/security/hideerrors.french.md | 25 + sections/security/limitrequests.french.md | 65 + sections/security/lintrules.french.md | 44 + sections/security/login-rate-limit.french.md | 41 + sections/security/non-root-user.french.md | 41 + sections/security/ormodmusage.french.md | 54 + sections/security/regex.french.md | 34 + .../requestpayloadsizelimit.french.md | 60 + sections/security/safemoduleloading.french.md | 15 + sections/security/saferedirects.french.md | 58 + sections/security/sandbox.french.md | 33 + sections/security/secretmanagement.french.md | 36 + sections/security/secureheaders.french.md | 172 +++ sections/security/secureserver.french.md | 28 + sections/security/sessions.french.md | 40 + sections/security/validation.french.md | 68 + .../3-parts-in-name.french.md | 54 + sections/testingandquality/aaa.french.md | 61 + .../avoid-global-test-fixture.french.md | 42 + .../testingandquality/bumpversion.french.md | 27 + sections/testingandquality/citools.french.md | 51 + .../testingandquality/refactoring.french.md | 43 + 70 files changed, 4634 insertions(+) create mode 100644 .operations/writing-guidelines.french.md create mode 100644 README.french.md create mode 100644 sections/codestylepractices/eslint_prettier.french.md create mode 100644 sections/errorhandling/apmproducts.french.md create mode 100644 sections/errorhandling/asyncerrorhandling.french.md create mode 100644 sections/errorhandling/catchunhandledpromiserejection.french.md create mode 100644 sections/errorhandling/centralizedhandling.french.md create mode 100644 sections/errorhandling/documentingusingswagger.french.md create mode 100644 sections/errorhandling/failfast.french.md create mode 100644 sections/errorhandling/monitoring.french.md create mode 100644 sections/errorhandling/operationalvsprogrammererror.french.md create mode 100644 sections/errorhandling/shuttingtheprocess.french.md create mode 100644 sections/errorhandling/testingerrorflows.french.md create mode 100644 sections/errorhandling/usematurelogger.french.md create mode 100644 sections/errorhandling/useonlythebuiltinerror.french.md create mode 100644 sections/performance/block-loop.french.md create mode 100644 sections/performance/nativeoverutil.french.md create mode 100644 sections/production/LTSrelease.french.md create mode 100644 sections/production/apmproducts.french.md create mode 100644 sections/production/assigntransactionid.french.md create mode 100644 sections/production/bestateless.french.md create mode 100644 sections/production/createmaintenanceendpoint.french.md create mode 100644 sections/production/delegatetoproxy.french.md create mode 100644 sections/production/detectvulnerabilities.french.md create mode 100644 sections/production/frontendout.french.md create mode 100644 sections/production/guardprocess.french.md create mode 100644 sections/production/lockdependencies.french.md create mode 100644 sections/production/logrouting.french.md create mode 100644 sections/production/measurememory.french.md create mode 100644 sections/production/monitoring.french.md create mode 100644 sections/production/productioncode.french.md create mode 100644 sections/production/setnodeenv.french.md create mode 100644 sections/production/smartlogging.french.md create mode 100644 sections/production/utilizecpu.french.md create mode 100644 sections/projectstructre/breakintcomponents.french.md create mode 100644 sections/projectstructre/configguide.french.md create mode 100644 sections/projectstructre/createlayers.french.md create mode 100644 sections/projectstructre/separateexpress.french.md create mode 100644 sections/projectstructre/thincomponents.french.md create mode 100644 sections/projectstructre/wraputilities.french.md create mode 100644 sections/security/avoid_publishing_secrets.french.md create mode 100644 sections/security/avoideval.french.md create mode 100644 sections/security/bcryptpasswords.french.md create mode 100644 sections/security/childprocesses.french.md create mode 100644 sections/security/commonsecuritybestpractices.french.md create mode 100644 sections/security/dependencysecurity.french.md create mode 100644 sections/security/escape-output.french.md create mode 100644 sections/security/expirejwt.french.md create mode 100644 sections/security/hideerrors.french.md create mode 100644 sections/security/limitrequests.french.md create mode 100644 sections/security/lintrules.french.md create mode 100644 sections/security/login-rate-limit.french.md create mode 100644 sections/security/non-root-user.french.md create mode 100644 sections/security/ormodmusage.french.md create mode 100644 sections/security/regex.french.md create mode 100644 sections/security/requestpayloadsizelimit.french.md create mode 100644 sections/security/safemoduleloading.french.md create mode 100644 sections/security/saferedirects.french.md create mode 100644 sections/security/sandbox.french.md create mode 100644 sections/security/secretmanagement.french.md create mode 100644 sections/security/secureheaders.french.md create mode 100644 sections/security/secureserver.french.md create mode 100644 sections/security/sessions.french.md create mode 100644 sections/security/validation.french.md create mode 100644 sections/testingandquality/3-parts-in-name.french.md create mode 100644 sections/testingandquality/aaa.french.md create mode 100644 sections/testingandquality/avoid-global-test-fixture.french.md create mode 100644 sections/testingandquality/bumpversion.french.md create mode 100644 sections/testingandquality/citools.french.md create mode 100644 sections/testingandquality/refactoring.french.md diff --git a/.operations/writing-guidelines.french.md b/.operations/writing-guidelines.french.md new file mode 100644 index 000000000..4234f6f26 --- /dev/null +++ b/.operations/writing-guidelines.french.md @@ -0,0 +1,31 @@ +# Our content writing manifest + +How we enhance the reading and learning experience for our visitors. + +## 1. Simple is better than better + +Making it easy to read and absorb knowledge is our mission, we curate content. As such we focus on transforming complex and exhausting topics into a simplified list, trade overloaded information with shortened and less-accurate details, avoid ‘flammable’ and controversial topics and escape subjective ideas in favor of generally accepted practices. + +## 2. Be evidence-based and reliable + +Our readers should have great confidence that the content they skim through is reliable. We achieve this by including evidence like references, data and other resources available to this topic. Practically, strive to include quotes from reliable sources, show benchmarks, related design patterns or any scientific measure to prove your claims. + +## 3. MECE (Mutually Exclusive and Collectively Exhaustive) + +Apart from the content being greatly edited and reliable, skimming through it should also provide full coverage of the topic. No important sub-topic should be left out. + +## 4. Consistent formatting + +The content is presented using fixed templates. Any future content must conform to the same template. If you wish to add new bullets copy a bullet format from an existing bullet and extend it to your needs. For additional information please view [this template](https://github.com/i0natan/nodebestpractices/blob/master/sections/template.md). + +## 5. It's About Node.js + +Each advice should be related directly to Node.js and not to software development in general. When we advise to implement generic pattern/rule in Node.js, the content should focus on the Node implementation. For example, when we advise to sanitize all requests input for security reasons, Node-lingo should be used - ‘Use middleware to sanitize request input’. If an item has no specific implementation in Node.js (e.g. it looks the same in Python & Jaba) - include it within a generic container item, see item 6.5 for example. + +## 6. Leading vendors only + +Sometimes it's useful to include names of vendors that can address certain challenges and problems like npm packages, open source tools or even commercial products. To avoid overwhelmingly long lists or recommending non-reputable and unstable projects, we came up with the following rules: + +- Only the top 3 vendors should be recommended – a vendor that appears in the top 3 results of a search engine (Google or GitHub sorted by popularity) for a given relevant keyword can be included in our recommendation. +- If it’s a npm package it must also be downloaded at least 750 times a day on average. +- If it’s an open-source project, it must have been updated at least once in the last 6 months. diff --git a/README.french.md b/README.french.md new file mode 100644 index 000000000..58d8b33b7 --- /dev/null +++ b/README.french.md @@ -0,0 +1,1150 @@ +[✔]: assets/images/checkbox-small-blue.png + +# Node.js Best Practices + +

+ Node.js Best Practices +

+ +
+ +
+ 85 items Last update: Oct 12, 2019 Updated for Node 12.12.0 +
+ +
+ +[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) + +
+ +Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) + +
+ +###### Built and maintained by our [Steering Committee](#steering-committee) and [Collaborators](#collaborators) + +# Latest Best Practices and News + +- **✅ New best practice:** 7.1: [Don't block the event loop](#7-draft-performance-best-practices) by Keith Holliday + +- **🇷🇺 Russian translation:** The amazing Alex Ivanov has just published a [Russian translation](/README.russian.md) + +- **We seek typescript contributors:** want to help contributing TypeScript examples? please approach by opening an issue + +

+ +# Welcome! 3 Things You Ought To Know First + +**1. You are, in fact, reading dozens of the best Node.js articles -** this repository is a summary and curation of the top-ranked content on Node.js best practices, as well as content written here by collaborators + +**2. It is the largest compilation, and it is growing every week -** currently, more than 80 best practices, style guides, and architectural tips are presented. New issues and pull requests are created every day to keep this live book updated. We'd love to see you contributing here, whether that is fixing code mistakes, helping with translations, or suggesting brilliant new ideas. See our [writing guidelines here](/.operations/writing-guidelines.md) + +**3. Most best practices have additional info -** most bullets include a **🔗Read More** link that expands on the practice with code examples, quotes from selected blogs and more information + +

+ +## Table of Contents + +1. [Project Structure Practices (5)](#1-project-structure-practices) +2. [Error Handling Practices (11) ](#2-error-handling-practices) +3. [Code Style Practices (12) ](#3-code-style-practices) +4. [Testing And Overall Quality Practices (12) ](#4-testing-and-overall-quality-practices) +5. [Going To Production Practices (18) ](#5-going-to-production-practices) +6. [Security Practices (25)](#6-security-best-practices) +7. [Performance Practices (2) (Work In Progress️ ✍️)](#7-draft-performance-best-practices) + +

+ +# `1. Project Structure Practices` + +## ![✔] 1.1 Structure your solution by components + +**TL;DR:** The worst large applications pitfall is maintaining a huge code base with hundreds of dependencies - such a monolith slows down developers as they try to incorporate new features. Instead, partition your code into components, each gets its own folder or a dedicated codebase, and ensure that each unit is kept small and simple. Visit 'Read More' below to see examples of correct project structure + +**Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependent components - deployments become slower and riskier. It's also considered harder to scale-out when all the business units are not separated + +🔗 [**Read More: structure by components**](/sections/projectstructre/breakintcomponents.md) + +

+ +## ![✔] 1.2 Layer your components, keep Express within its boundaries + +**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic, and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (Express req, res) to business logic and data layers - this makes your application dependent on and accessible by Express only + +**Otherwise:** App that mixes web objects with other layers cannot be accessed by testing code, CRON jobs, and other non-Express callers + +🔗 [**Read More: layer your app**](/sections/projectstructre/createlayers.md) + +

+ +## ![✔] 1.3 Wrap common utilities as npm packages + +**TL;DR:** In a large app that constitutes a large code base, cross-cutting-concern utilities like logger, encryption and alike, should be wrapped by your own code and exposed as private npm packages. This allows sharing them among multiple code bases and projects + +**Otherwise:** You'll have to invent your own deployment and dependency wheel + +🔗 [**Read More: Structure by feature**](/sections/projectstructre/wraputilities.md) + +

+ +## ![✔] 1.4 Separate Express 'app' and 'server' + +**TL;DR:** Avoid the nasty habit of defining the entire [Express](https://expressjs.com/) app in a single huge file - separate your 'Express' definition to at least two files: the API declaration (app.js) and the networking concerns (WWW). For even better structure, locate your API declaration within components + +**Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file + +🔗 [**Read More: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.md) + +

+ +## ![✔] 1.5 Use environment aware, secure and hierarchical config + +**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) + +**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both + +🔗 [**Read More: configuration best practices**](/sections/projectstructre/configguide.md) + +


+ +

⬆ Return to top

+ +# `2. Error Handling Practices` + +## ![✔] 2.1 Use Async-Await or promises for async error handling + +**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using a reputable promise library or async-await instead which enables a much more compact and familiar code syntax like try-catch + +**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns + +🔗 [**Read More: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.md) + +

+ +## ![✔] 2.2 Use only the built-in Error object + +**TL;DR:** Many throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Whether you reject a promise, throw an exception or emit an error – using only the built-in Error object will increase uniformity and prevent loss of information + +**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! + +🔗 [**Read More: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.md) + +

+ +## ![✔] 2.3 Distinguish operational vs programmer errors + +**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, programmer error (e.g. trying to read undefined variable) refers to unknown code failures that dictate to gracefully restart the application + +**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context + +🔗 [**Read More: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.md) + +

+ +## ![✔] 2.4 Handle errors centrally, not within an Express middleware + +**TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in + +**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors + +🔗 [**Read More: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.md) + +

+ +## ![✔] 2.5 Document API errors using Swagger or GraphQL + +**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like Swagger. If you're using GraphQL, you can utilize your schema and comments as well. + +**Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) + +🔗 [**Read More: documenting API errors in Swagger or GraphQL**](/sections/errorhandling/documentingusingswagger.md) + +

+ +## ![✔] 2.6 Exit the process gracefully when a stranger comes to town + +**TL;DR:** When an unknown error occurs (a developer error, see best practice 2.3) - there is uncertainty about the application healthiness. A common practice suggests restarting the process carefully using a process management tool like [Forever](https://www.npmjs.com/package/forever) or [PM2](http://pm2.keymetrics.io/) + +**Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily + +🔗 [**Read More: shutting the process**](/sections/errorhandling/shuttingtheprocess.md) + +

+ +## ![✔] 2.7 Use a mature logger to increase error visibility + +**TL;DR:** A set of mature logging tools like [Winston](https://www.npmjs.com/package/winston), [Bunyan](https://github.com/trentm/node-bunyan), [Log4js](http://stritti.github.io/log4js/) or [Pino](https://github.com/pinojs/pino), will speed-up error discovery and understanding. So forget about console.log + +**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late + +🔗 [**Read More: using a mature logger**](/sections/errorhandling/usematurelogger.md) + +

+ +## ![✔] 2.8 Test error flows using your favorite test framework + +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. Testing frameworks like Mocha & Chai can handle this easily (see code examples within the "Gist popup") + +**Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling + +🔗 [**Read More: testing error flows**](/sections/errorhandling/testingerrorflows.md) + +

+ +## ![✔] 2.9 Discover errors and downtime using APM products + +**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes and slow parts that you were missing + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: using APM products**](/sections/errorhandling/apmproducts.md) + +

+ +## ![✔] 2.10 Catch unhandled promise rejections + +**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` + +**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about + +🔗 [**Read More: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.md) + +

+ +## ![✔] 2.11 Fail fast, validate arguments using a dedicated library + +**TL;DR:** This should be part of your Express best practices – Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like Joi + +**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? + +🔗 [**Read More: failing fast**](/sections/errorhandling/failfast.md) + +


+ +

⬆ Return to top

+ +# `3. Code Style Practices` + +## ![✔] 3.1 Use ESLint + +**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint + +**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style + +🔗 [**Read More: Using ESLint and Prettier**](/sections/codestylepractices/eslint_prettier.md) + +

+ +## ![✔] 3.2 Node.js specific plugins + +**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) + +**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early + +

+ +## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line + +**TL;DR:** The opening curly braces of a code block should be on the same line as the opening statement + +### Code Example + +```javascript +// Do +function someFunction() { + // code block +} + +// Avoid +function someFunction() +{ + // code block +} +``` + +**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: + +🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) + +

+ +## ![✔] 3.4 Separate your statements properly + +No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. + +**TL;DR:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. + +**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediate invoked function expressions to prevent most of unexpected errors. + +### Code example + +```javascript +// Do +function doThing() { + // ... +} + +doThing() + +// Do + +const items = [1, 2, 3] +items.forEach(console.log) + +// Avoid — throws exception +const m = new Map() +const a = [1,2,3] +[...m.values()].forEach(console.log) +> [...m.values()].forEach(console.log) +> ^^^ +> SyntaxError: Unexpected token ... + +// Avoid — throws exception +const count = 2 // it tries to run 2(), but 2 is not a function +(function doSomething() { + // do something amazing +}()) +// put a semicolon before the immediate invoked function, after the const definition, save the return value of the anonymous function to a variable or avoid IIFEs alltogether +``` + +🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) +🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline) + +

+ +## ![✔] 3.5 Name your functions + +**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot + +**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions + +

+ +## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes + +**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions and **_UpperCamelCase_** (capital first letter as well) when naming classes. This will help you to easily distinguish between plain variables/functions, and classes that require instantiation. Use descriptive names, but try to keep them short + +**Otherwise:** Javascript is the only language in the world which allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase + +### 3.6 Code Example + +```javascript +// for class name we use UpperCamelCase +class SomeClassExample {} + +// for const names we use the const keyword and lowerCamelCase +const config = { + key: 'value' +}; + +// for variables and functions names we use lowerCamelCase +let someVariableExample = 'value'; +function doSomething() {} +``` + +

+ +## ![✔] 3.7 Prefer const over let. Ditch the var + +**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal + +**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes + +🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) + +

+ +## ![✔] 3.8 Require modules first, not inside functions + +**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems + +**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its own dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function + +

+ +## ![✔] 3.9 Require modules by folders, opposed to the files directly + +**TL;DR:** When developing a module/library in a folder, place an index.js file that exposes the module's internals so every consumer will pass through it. This serves as an 'interface' to your module and eases future changes without breaking the contract + +**Otherwise:** Changing the internal structure of files or the signature may break the interface with clients + +### 3.9 Code example + +```javascript +// Do +module.exports.SMSProvider = require('./SMSProvider'); +module.exports.SMSNumberResolver = require('./SMSNumberResolver'); + +// Avoid +module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js'); +module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js'); +``` + +

+ +## ![✔] 3.10 Use the `===` operator + +**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal + +**Otherwise:** Unequal variables might return true when compared with the `==` operator + +### 3.10 Code example + +```javascript +'' == '0' // false +0 == '' // true +0 == '0' // true + +false == 'false' // false +false == '0' // true + +false == undefined // false +false == null // false +null == undefined // true + +' \t\r\n ' == 0 // true +``` + +All statements above will return false if used with `===` + +

+ +## ![✔] 3.11 Use Async Await, avoid callbacks + +**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch + +**Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and makes it difficult to reason about the code flow + +🔗[**Read more:** Guide to async await 1.0](https://github.com/yortus/asyncawait) + +

+ +## ![✔] 3.12 Use arrow function expressions (=>) + +**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) + +**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read + +🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) + +


+ +

⬆ Return to top

+ +# `4. Testing And Overall Quality Practices` + +## ![✔] 4.1 At the very least, write API (component) testing + +**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/). Afterward, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc + +**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage + +

+ +## ![✔] 4.2 Include 3 parts in each test name + +**TL;DR:** Make the test speak at the requirements level so it's self explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances and what is the expected result + +**Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? + +🔗 [**Read More: Include 3 parts in each test name**](/sections/testingandquality/3-parts-in-name.md) + +

+ +## ![✔] 4.3 Structure tests by the AAA pattern + +**TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan + +**Otherwise:** Not only you spend long daily hours on understanding the main code, now also what should have been the simple part of the day (testing) stretches your brain + +🔗 [**Read More: Structure tests by the AAA pattern**](/sections/testingandquality/aaa.md) + +

+ +## ![✔] 4.4 Detect code issues with a linter + +**TL;DR:** Use a code linter to check basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](#3-code-style-practices) on Code Style Practices + +**Otherwise:** You may let pass some anti-pattern and possible vulnerable code to your production environment. + +

+ +## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test + +**TL;DR:** To prevent tests coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records + +**Otherwise:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build + +🔗 [**Read More: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md) + +

+ +## ![✔] 4.6 Constantly inspect for vulnerable dependencies + +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [npm audit](https://docs.npmjs.com/cli/audit) and 🔗 [snyk.io](https://snyk.io) that can be invoked from your CI on every build + +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious + +

+ +## ![✔] 4.7 Tag your tests + +**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' + +**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests + +

+ +## ![✔] 4.8 Check your test coverage, it helps to identify wrong test patterns + +**TL;DR:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold + +**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing + +

+ +## ![✔] 4.9 Inspect for outdated packages + +**TL;DR:** Use your preferred tool (e.g. 'npm outdated' or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) to detect installed packages which are outdated, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version + +**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky + +

+ +## ![✔] 4.10 Use production-like env for e2e testing + +**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as closed to your real production as possible like a-continue + +**Otherwise:** Without docker-compose teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments + +

+ +## ![✔] 4.11 Refactor regularly using static analysis tools + +**TL;DR:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity) and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). + +**Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix + +🔗 [**Read More: Refactoring!**](/sections/testingandquality/refactoring.md) + +

+ +## ![✔] 4.12 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) + +**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of complex setup that demands a steep learning curve. Nowadays, it has become much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully + +**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup + +🔗 [**Read More: Choosing CI platform**](/sections/testingandquality/citools.md) + +


+ +

⬆ Return to top

+ +# `5. Going To Production Practices` + +## ![✔] 5.1. Monitoring + +**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions + +**Otherwise:** Failure === disappointed customers. Simple + +🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md) + +

+ +## ![✔] 5.2. Increase transparency using smart logging + +**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted + +**Otherwise:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information + +🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md) + +

+ +## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy + +**TL;DR:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead + +**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly + +🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md) + +

+ +## ![✔] 5.4. Lock dependencies + +**TL;DR:** Your code must be identical across all environments, but amazingly npm lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using npm config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grained control use `npm shrinkwrap`. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default + +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code + +🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md) + +

+ +## ![✔] 5.5. Guard process uptime using the right tool + +**TL;DR:** The process must go on and get restarted upon failures. For simple scenarios, process management tools like PM2 might be enough but in today's ‘dockerized’ world, cluster management tools should be considered as well + +**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos + +🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md) + +

+ +## ![✔] 5.6. Utilize all CPU cores + +**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) + +**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) + +🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md) + +

+ +## ![✔] 5.7. Create a ‘maintenance endpoint’ + +**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tests tools, some valuable information and operations are easier done using code + +**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes + +🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md) + +

+ +## ![✔] 5.8. Discover errors and downtime using APM products + +**TL;DR:** Application monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md) + +

+ +## ![✔] 5.9. Make your code production-ready + +**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) + +**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written + +🔗 [**Read More: Make your code production-ready**](/sections/production/productioncode.md) + +

+ +## ![✔] 5.10. Measure and guard the memory usage + +**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system + +**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) + +🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md) + +

+ +## ![✔] 5.11. Get your frontend assets out of Node + +**TL;DR:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single-threaded model + +**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content + +🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md) + +

+ +## ![✔] 5.12. Be stateless, kill your servers almost every day + +**TL;DR:** Store any type of data (e.g. user sessions, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior + +**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server + +🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md) + +

+ +## ![✔] 5.13. Use tools that automatically detect vulnerabilities + +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately + +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious + +🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md) + +

+ +## ![✔] 5.14. Assign a transaction id to each log statement + +**TL;DR:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside + +**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue + +🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md) + +

+ +## ![✔] 5.15. Set NODE_ENV=production + +**TL;DR:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many npm packages determine the current environment and optimize their code for production + +**Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! + +🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md) + +

+ +## ![✔] 5.16. Design automated, atomic and zero-downtime deployments + +**TL;DR:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment + +**Otherwise:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features + +

+ +## ![✔] 5.17. Use an LTS release of Node.js + +**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements + +**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain + +🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md) + +

+ +## ![✔] 5.18. Don't route logs within the app + +**TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). + +**Otherwise:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns + +🔗 [**Read More: Log Routing**](/sections/production/logrouting.md) + +


+ +

⬆ Return to top

+ +# `6. Security Best Practices` + +
+54 items +
+ +## ![✔] 6.1. Embrace linter security rules + + + +**TL;DR:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter + +**Otherwise:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories + +🔗 [**Read More: Lint rules**](/sections/security/lintrules.md) + +

+ +## ![✔] 6.2. Limit concurrent requests using a middleware + + + +**TL;DR:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) + +**Otherwise:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. + +🔗 [**Read More: Implement rate limiting**](/sections/security/limitrequests.md) + +

+ +## ![✔] 6.3 Extract secrets from config files or use packages to encrypt them + + + +**TL;DR:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally + +**Otherwise:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). + +🔗 [**Read More: Secret management**](/sections/security/secretmanagement.md) + +

+ +## ![✔] 6.4. Prevent query injection vulnerabilities with ORM/ODM libraries + + + +**TL;DR:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. + +**Otherwise:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. + +🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md) + +

+ +## ![✔] 6.5. Collection of generic security best practices + +**TL;DR:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Common security best practices**](/sections/security/commonsecuritybestpractices.md) + +

+ +## ![✔] 6.6. Adjust the HTTP response headers for enhanced security + + + +**TL;DR:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). + +**Otherwise:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities + +🔗 [**Read More: Using secure headers in your application**](/sections/security/secureheaders.md) + +

+ +## ![✔] 6.7. Constantly and automatically inspect for vulnerable dependencies + + + +**TL;DR:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. + +**Otherwise:** An attacker could detect your web framework and attack all its known vulnerabilities. + +🔗 [**Read More: Dependency security**](/sections/security/dependencysecurity.md) + +

+ +## ![✔] 6.8. Avoid using the Node.js crypto library for handling passwords, use Bcrypt + + + +**TL;DR:** Passwords or secrets (API keys) should be stored using a secure hash + salt function like `bcrypt`, that should be a preferred choice over its JavaScript implementation due to performance and security reasons. + +**Otherwise:** Passwords or secrets that are persisted without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. + +🔗 [**Read More: Use Bcrypt**](/sections/security/bcryptpasswords.md) + +

+ +## ![✔] 6.9. Escape HTML, JS and CSS output + + + +**TL;DR:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) + +**Otherwise:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients + +🔗 [**Read More: Escape output**](/sections/security/escape-output.md) + +

+ +## ![✔] 6.10. Validate incoming JSON schemas + + + +**TL;DR:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) + +**Otherwise:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application + +🔗 [**Read More: Validate incoming JSON schemas**](/sections/security/validation.md) + +

+ +## ![✔] 6.11. Support blacklisting JWTs + + + +**TL;DR:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blacklist of untrusted tokens that are validated on each request. + +**Otherwise:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. + +🔗 [**Read More: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md) + +

+ +## ![✔] 6.12. Prevent brute-force attacks against authorization + + + +**TL;DR:** A simple and powerful technique is to limit authorization attempts using two metrics: + +1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. +2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. + +**Otherwise:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application + +🔗 [**Read More: Login rate limiting**](/sections/security/login-rate-limit.md) + +

+ +## ![✔] 6.13. Run Node.js as non-root user + + + +**TL;DR:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" + +**Otherwise:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to his server) + +🔗 [**Read More: Run Node.js as non-root user**](/sections/security/non-root-user.md) + +

+ +## ![✔] 6.14. Limit payload size using a reverse-proxy or a middleware + + + +**TL;DR:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads + +**Otherwise:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks + +🔗 [**Read More: Limit payload size**](/sections/security/requestpayloadsizelimit.md) + +

+ +## ![✔] 6.15. Avoid JavaScript eval statements + + + +**TL;DR:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. + +**Otherwise:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. + +🔗 [**Read More: Avoid JavaScript eval statements**](/sections/security/avoideval.md) + +

+ +## ![✔] 6.16. Prevent evil RegEx from overloading your single thread execution + + + +**TL;DR:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns + +**Otherwise:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 + +🔗 [**Read More: Prevent malicious RegEx**](/sections/security/regex.md) + +

+ +## ![✔] 6.17. Avoid module loading using a variable + + + +**TL;DR:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough + +**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the filesystem, or access already existing system files. + +🔗 [**Read More: Safe module loading**](/sections/security/safemoduleloading.md) + +

+ +## ![✔] 6.18. Run unsafe code in a sandbox + + + +**TL;DR:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox + +**Otherwise:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables + +🔗 [**Read More: Run unsafe code in a sandbox**](/sections/security/sandbox.md) + +

+ +## ![✔] 6.19. Take extra care when working with child processes + + + +**TL;DR:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. + +**Otherwise:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. + +🔗 [**Read More: Be cautious when working with child processes**](/sections/security/childprocesses.md) + +

+ +## ![✔] 6.20. Hide error details from clients + + + +**TL;DR:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details + +**Otherwise:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace + +🔗 [**Read More: Hide error details from client**](/sections/security/hideerrors.md) + +

+ +## ![✔] 6.21. Configure 2FA for npm or Yarn + + + +**TL;DR:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. + +**Otherwise:** [Have you heard about the eslint developer who's password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) + +

+ +## ![✔] 6.22. Modify session middleware settings + + + +**TL;DR:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) + +**Otherwise:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities + +🔗 [**Read More: Cookie and session security**](/sections/security/sessions.md) + +

+ +## ![✔] 6.23. Avoid DOS attacks by explicitly setting when a process should crash + + + +**TL;DR:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) + +**Otherwise:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease + +

+ +## ![✔] 6.24. Prevent unsafe redirects + + + +**TL;DR:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. + +**Otherwise:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. + +🔗 [**Read More: Prevent unsafe redirects**](/sections/security/saferedirects.md) + +

+ +## ![✔] 6.25. Avoid publishing secrets to the npm registry + + + +**TL;DR:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. + +**Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. + +🔗 [**Read More: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md) +


+ +

⬆ Return to top

+ +# `7. Draft: Performance Best Practices` + +## Our contributors are working on this section. [Would you like to join?](https://github.com/i0natan/nodebestpractices/issues/256) + +

+ +## ![✔] 7.1. Don't block the event loop + +**TL;DR:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. + +**Otherwise:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** + +🔗 [**Read More: Do not block the event loop**](/sections/performance/block-loop.md) + +


+ + +## ![✔] 7.2. Prefer native JS methods over user-land utils like Lodash + + **TL;DR:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. + Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. + +**Otherwise:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. + +🔗 [**Read More: Native over user land utils**](/sections/performance/nativeoverutil.md) + +


+ + +# Milestones + +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project + +
+ +## Translations + +All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! + +### Completed translations + +- ![BR](/assets/flags/BR.png) [Brazilian Portuguese](./README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) +- ![CN](/assets/flags/CN.png) [Chinese](./README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) +- ![RU](/assets/flags/RU.png) [Russian](./README.russian.md) - Courtesy of [Alex Ivanov](https://github.com/contributorpw) + +### Translations in progress + +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) + +

+ +## Steering Committee + +Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/i0natan/nodebestpractices/projects). + + + +[Yoni Goldberg](https://github.com/i0natan) + + + +Independent Node.js consultant who works with customers in USA, Europe, and Israel on building large scale scalable Node applications. Many of the best practices above were first published at [goldbergyoni.com](https://goldbergyoni.com). Reach Yoni at @goldbergyoni or me@goldbergyoni.com + +
+ + + +[Bruno Scheufler](https://github.com/BrunoScheufler) + + +💻 full-stack web engineer, Node.js & GraphQL enthusiast + +
+ + + +[Kyle Martin](https://github.com/js-kyle) + + + +Full Stack Developer & Site Reliability Engineer based in New Zealand, interested in web application security, and architecting and building Node.js applications to perform at global scale. + +
+ + + +[Sagir Khan](https://github.com/sagirk) + + + + +Deep specialist in JavaScript and its ecosystem — React, Node.js, MongoDB, pretty much anything that involves using JavaScript/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation, collaborating on the Community Committee's Website Redesign Initiative. + +
+ +## Collaborators + +Thank you to all our collaborators! 🙏 + +Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 + +| | | +| :--: | :--: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | + +### Past collaborators + +| | +| :--: | +| [Refael Ackermann](https://github.com/refack) | + +
+ +## Thank You Notes + +We appreciate any contribution, from a single word fix to a new best practice. View our contributors and [contributing documentation here!](CONTRIBUTORS.md) + +


diff --git a/sections/codestylepractices/eslint_prettier.french.md b/sections/codestylepractices/eslint_prettier.french.md new file mode 100644 index 000000000..698857dbc --- /dev/null +++ b/sections/codestylepractices/eslint_prettier.french.md @@ -0,0 +1,26 @@ +# Using ESLint and Prettier + + +### Comparing ESLint and Prettier + +If you format this code using ESLint, it will just give you a warning that it's too wide (depends on your `max-len` setting). Prettier will automatically format it for you. + +```javascript +foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne(), noWayYouGottaBeKiddingMe()); +``` + +```javascript +foo( + reallyLongArg(), + omgSoManyParameters(), + IShouldRefactorThis(), + isThereSeriouslyAnotherOne(), + noWayYouGottaBeKiddingMe() +); +``` + +Source: [https://github.com/prettier/prettier-eslint/issues/101](https://github.com/prettier/prettier-eslint/issues/101) + +### Integrating ESLint and Prettier + +ESLint and Prettier overlap in the code formatting feature but can be easily combined by using other packages like [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), and [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). For more information about their differences, you can view the link [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). diff --git a/sections/errorhandling/apmproducts.french.md b/sections/errorhandling/apmproducts.french.md new file mode 100644 index 000000000..b429ef77a --- /dev/null +++ b/sections/errorhandling/apmproducts.french.md @@ -0,0 +1,28 @@ +# Discover errors and downtime using APM products + + +### One Paragraph Explainer + +Exception != Error. Traditional error handling assumes the existence of Exception but application errors might come in the form of slow code paths, API downtime, lack of computational resources and more. This is where APM products come in handy as they allow to detect a wide variety of ‘burried’ issues proactively with a minimal setup. Among the common features of APM products are for example alerting when the HTTP API returns errors, detect when the API response time drops below some threshold, detection of ‘code smells’, features to monitor server resources, operational intelligence dashboard with IT metrics and many other useful features. Most vendors offer a free plan. + +### Wikipedia about APM + +In the fields of information technology and systems management, Application Performance Management (APM) is the monitoring and management of performance and availability of software applications. APM strives to detect and diagnose complex application performance problems to maintain an expected level of service. APM is “the translation of IT metrics into business meaning ([i.e.] value)". Major products and segments. + +### Understanding the APM marketplace + +APM products constitute 3 major segments: + +1. Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests. Can be set up in few minutes. Following are few selected contenders: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), and [New Relic](https://newrelic.com/application-monitoring) + +2. Code instrumentation – product family which requires embedding an agent within the application to use features like slow code detection, exception statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics + +3. Operational intelligence dashboard – this line of products is focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/) + + + + ### Example: UpTimeRobot.Com – Website monitoring dashboard +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") + + ### Example: AppDynamics.Com – end to end monitoring combined with code instrumentation +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/asyncerrorhandling.french.md b/sections/errorhandling/asyncerrorhandling.french.md new file mode 100644 index 000000000..637be68b8 --- /dev/null +++ b/sections/errorhandling/asyncerrorhandling.french.md @@ -0,0 +1,109 @@ +# Use Async-Await or promises for async error handling + +### One Paragraph Explainer + +Callbacks don’t scale well since most programmers are not familiar with them. They force to check errors all over, deal with nasty code nesting and make it difficult to reason about the code flow. Promise libraries like BlueBird, async, and Q pack a standard code style using RETURN and THROW to control the program flow. Specifically, they support the favorite try-catch error handling style which allows freeing the main code path from dealing with errors in every function + +### Code Example – using promises to catch errors + +```javascript +return functionA() + .then(functionB) + .then(functionC) + .then(functionD) + .catch((err) => logger.error(err)) + .then(alwaysExecuteThisFunction) +``` + + +### Code Example - using async/await to catch errors + +```javascript +async function executeAsyncTask () { + try { + const valueA = await functionA(); + const valueB = await functionB(valueA); + const valueC = await functionC(valueB); + return await functionD(valueC); + } + catch (err) { + logger.error(err); + } finally { + await alwaysExecuteThisFunction(); + } +} +``` + +### Anti pattern code example – callback style error handling + +
+Javascript + +```javascript +getData(someParameter, function(err, result) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(a, function(err, result) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(b, function(c) { + getMoreData(d, function(e) { + if(err !== null ) { + // you get the idea? + } + }) + }); + } + }); + } +}); +``` +
+ +
+Typescript + +```typescript +getData(someParameter, function(err: Error | null, resultA: ResultA) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(resultA, function(err: Error | null, resultB: ResultB) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(resultB, function(resultC: ResultC) { + getMoreData(resultC, function(err: Error | null, d: ResultD) { + if(err !== null) { + // you get the idea? + } + }) + }); + } + }); + } +}); +``` +
+ +### Blog Quote: "We have a problem with promises" + + From the blog pouchdb.com + + > ……And in fact, callbacks do something even more sinister: they deprive us of the stack, which is something we usually take for granted in programming languages. Writing code without a stack is a lot like driving a car without a brake pedal: you don’t realize how badly you need it until you reach for it and it’s not there. The whole point of promises is to give us back the language fundamentals we lost when we went async: return, throw, and the stack. But you have to know how to use promises correctly in order to take advantage of them. + +### Blog Quote: "The promises method is much more compact" + + From the blog gosquared.com + + > ………The promises method is much more compact, clearer and quicker to write. If an error or exception occurs within any of the ops it is handled by the single .catch() handler. Having this single place to handle all errors means you don’t need to write error checking for each stage of the work. + +### Blog Quote: "Promises are native ES6, can be used with generators" + + From the blog StrongLoop + + > ….Callbacks have a lousy error-handling story. Promises are better. Marry the built-in error handling in Express with promises and significantly lower the chances of an uncaught exception. Promises are native ES6, can be used with generators, and ES7 proposals like async/await through compilers like Babel + +### Blog Quote: "All those regular flow control constructs you are used to are completely broken" + +From the blog Benno’s + + > ……One of the best things about asynchronous, callback-based programming is that basically all those regular flow control constructs you are used to are completely broken. However, the one I find most broken is the handling of exceptions. Javascript provides a fairly familiar try…catch construct for dealing with exceptions. The problem with exceptions is that they provide a great way of short-cutting errors up a call stack, but end up being completely useless if the error happens on a different stack… diff --git a/sections/errorhandling/catchunhandledpromiserejection.french.md b/sections/errorhandling/catchunhandledpromiserejection.french.md new file mode 100644 index 000000000..55ef84a20 --- /dev/null +++ b/sections/errorhandling/catchunhandledpromiserejection.french.md @@ -0,0 +1,87 @@ +# Catch unhandled promise rejections + +

+ +### One Paragraph Explainer + +Typically, most of modern Node.js/Express application code runs within promises – whether within the .then handler, a function callback or in a catch block. Surprisingly, unless a developer remembered to add a .catch clause, errors thrown at these places are not handled by the uncaughtException event-handler and disappear. Recent versions of Node added a warning message when an unhandled rejection pops, though this might help to notice when things go wrong but it's obviously not a proper error handling method. The straightforward solution is to never forget adding .catch clauses within each promise chain call and redirect to a centralized error handler. However, building your error handling strategy only on developer’s discipline is somewhat fragile. Consequently, it’s highly recommended using a graceful fallback and subscribe to `process.on('unhandledRejection', callback)` – this will ensure that any promise error, if not handled locally, will get its treatment. + +

+ +### Code example: these errors will not get caught by any error handler (except unhandledRejection) + +```javascript +DAL.getUserById(1).then((johnSnow) => { + // this error will just vanish + if(johnSnow.isAlive === false) + throw new Error('ahhhh'); +}); +``` + +

+ +### Code example: Catching unresolved and rejected promises + +
+Javascript + +```javascript +process.on('unhandledRejection', (reason, p) => { + // I just caught an unhandled promise rejection, + // since we already have fallback handler for unhandled errors (see below), + // let throw and let him handle that + throw reason; +}); + +process.on('uncaughtException', (error) => { + // I just received an error that was never handled, time to handle it and then decide whether a restart is needed + errorManagement.handler.handleError(error); + if (!errorManagement.handler.isTrustedError(error)) + process.exit(1); +}); +``` +
+ +
+Typescript + +```typescript +process.on('unhandledRejection', (reason: string, p: Promise) => { + // I just caught an unhandled promise rejection, + // since we already have fallback handler for unhandled errors (see below), + // let throw and let him handle that + throw reason; +}); + +process.on('uncaughtException', (error: Error) => { + // I just received an error that was never handled, time to handle it and then decide whether a restart is needed + errorManagement.handler.handleError(error); + if (!errorManagement.handler.isTrustedError(error)) + process.exit(1); +}); +``` +
+ +

+ +### Blog Quote: "If you can make a mistake, at some point you will" + + From the blog James Nelson + + > Let’s test your understanding. Which of the following would you expect to print an error to the console? + +```javascript +Promise.resolve('promised value').then(() => { + throw new Error('error'); +}); + +Promise.reject('error value').catch(() => { + throw new Error('error'); +}); + +new Promise((resolve, reject) => { + throw new Error('error'); +}); +``` + +> I don’t know about you, but my answer is that I’d expect all of them to print an error. However, the reality is that a number of modern JavaScript environments won’t print errors for any of them.The problem with being human is that if you can make a mistake, at some point you will. Keeping this in mind, it seems obvious that we should design things in such a way that mistakes hurt as little as possible, and that means handling errors by default, not discarding them. diff --git a/sections/errorhandling/centralizedhandling.french.md b/sections/errorhandling/centralizedhandling.french.md new file mode 100644 index 000000000..1d184626d --- /dev/null +++ b/sections/errorhandling/centralizedhandling.french.md @@ -0,0 +1,164 @@ +# Handle errors centrally. Not within middlewares + +### One Paragraph Explainer + +Without one dedicated object for error handling, greater are the chances of important errors hiding under the radar due to improper handling. The error handler object is responsible for making the error visible, for example by writing to a well-formatted logger, sending events to some monitoring product like [Sentry](https://sentry.io/), [Rollbar](https://rollbar.com/), or [Raygun](https://raygun.com/). Most web frameworks, like [Express](http://expressjs.com/en/guide/error-handling.html#writing-error-handlers), provide an error handling middleware mechanism. A typical error handling flow might be: Some module throws an error -> API router catches the error -> it propagates the error to the middleware (e.g. Express, KOA) who is responsible for catching errors -> a centralized error handler is called -> the middleware is being told whether this error is an untrusted error (not operational) so it can restart the app gracefully. Note that it’s a common, yet wrong, practice to handle errors within Express middleware – doing so will not cover errors that are thrown in non-web interfaces. + +### Code Example – a typical error flow + +
+Javascript + +```javascript +// DAL layer, we don't handle errors here +DB.addDocument(newCustomer, (error, result) => { + if (error) + throw new Error('Great error explanation comes here', other useful parameters) +}); + +// API route code, we catch both sync and async errors and forward to the middleware +try { + customerService.addNew(req.body).then((result) => { + res.status(200).json(result); + }).catch((error) => { + next(error) + }); +} +catch (error) { + next(error); +} + +// Error handling middleware, we delegate the handling to the centralized error handler +app.use(async (err, req, res, next) => { + const isOperationalError = await errorHandler.handleError(err); + if (!isOperationalError) { + next(err); + } +}); +``` +
+ +
+Typescript + +```typescript +// DAL layer, we don't handle errors here +DB.addDocument(newCustomer, (error: Error, result: Result) => { + if (error) + throw new Error('Great error explanation comes here', other useful parameters) +}); + +// API route code, we catch both sync and async errors and forward to the middleware +try { + customerService.addNew(req.body).then((result: Result) => { + res.status(200).json(result); + }).catch((error: Error) => { + next(error) + }); +} +catch (error) { + next(error); +} + +// Error handling middleware, we delegate the handling to the centralized error handler +app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => { + const isOperationalError = await errorHandler.handleError(err); + if (!isOperationalError) { + next(err); + } +}); +``` +
+ + +### Code example – handling errors within a dedicated object + +
+Javascript + +```javascript +module.exports.handler = new errorHandler(); + +function errorHandler() { + this.handleError = async (err) => { + await logger.logError(err); + await sendMailToAdminIfCritical; + await saveInOpsQueueIfCritical; + await determineIfOperationalError; + }; +} +``` +
+ +
+Typescript + +```typescript +class ErrorHandler { + public async handleError(err: Error): Promise { + await logger.logError(err); + await sendMailToAdminIfCritical(); + await saveInOpsQueueIfCritical(); + await determineIfOperationalError(); + }; +} + +export const handler = new ErrorHandler(); +``` +
+ + +### Code Example – Anti Pattern: handling errors within the middleware + +
+Javascript + +```javascript +// middleware handling the error directly, who will handle Cron jobs and testing errors? +app.use((err, req, res, next) => { + logger.logError(err); + if (err.severity == errors.high) { + mailer.sendMail(configuration.adminMail, 'Critical error occured', err); + } + if (!err.isOperational) { + next(err); + } +}); +``` +
+ + +
+Typescript + +```typescript +// middleware handling the error directly, who will handle Cron jobs and testing errors? +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + logger.logError(err); + if (err.severity == errors.high) { + mailer.sendMail(configuration.adminMail, 'Critical error occured', err); + } + if (!err.isOperational) { + next(err); + } +}); +``` +
+ +### Blog Quote: "Sometimes lower levels can’t do anything useful except propagate the error to their caller" + +From the blog Joyent, ranked 1 for the keywords “Node.js error handling” + +> …You may end up handling the same error at several levels of the stack. This happens when lower levels can’t do anything useful except propagate the error to their caller, which propagates the error to its caller, and so on. Often, only the top-level caller knows what the appropriate response is, whether that’s to retry the operation, report an error to the user, or something else. But that doesn’t mean you should try to report all errors to a single top-level callback, because that callback itself can’t know in what context the error occurred… + +### Blog Quote: "Handling each err individually would result in tremendous duplication" + +From the blog JS Recipes ranked 17 for the keywords “Node.js error handling” + +> ……In Hackathon Starter api.js controller alone, there are over 79 occurrences of error objects. Handling each err individually would result in a tremendous amount of code duplication. The next best thing you can do is to delegate all error handling logic to an Express middleware… + +### Blog Quote: "HTTP errors have no place in your database code" + +From the blog Daily JS ranked 14 for the keywords “Node.js error handling” + +> ……You should set useful properties in error objects, but use such properties consistently. And, don’t cross the streams: HTTP errors have no place in your database code. Or for browser developers, Ajax errors have a place in the code that talks to the server, but not code that processes Mustache templates… diff --git a/sections/errorhandling/documentingusingswagger.french.md b/sections/errorhandling/documentingusingswagger.french.md new file mode 100644 index 000000000..125b58a91 --- /dev/null +++ b/sections/errorhandling/documentingusingswagger.french.md @@ -0,0 +1,52 @@ +# Document API errors using Swagger or GraphQL + +### One Paragraph Explainer + +REST APIs return results using HTTP status codes, it’s absolutely required for the API user to be aware not only about the API schema but also about potential errors – the caller may then catch an error and tactfully handle it. For example, your API documentation might state in advance that HTTP status 409 is returned when the customer name already exists (assuming the API register new users) so the caller can correspondingly render the best UX for the given situation. Swagger is a standard that defines the schema of API documentation offering an eco-system of tools that allow creating documentation easily online, see print screens below + +If you have already adopted GraphQL for your API endpoints, your schema already contains strict guarantees as to what errors should look like ([outlined in the spec](https://facebook.github.io/graphql/June2018/#sec-Errors)) and how they should be handled by your client-side tooling. In addition, you can also supplement them with comment-based documentation. + +### GraphQL Error Example + +> This example uses [SWAPI](https://graphql.org/swapi-graphql), the Star Wars API. + +```graphql +# should fail because id is not valid +{ + film(id: "1ZmlsbXM6MQ==") { + title + } +} +``` + +```json +{ + "errors": [ + { + "message": "No entry in local cache for https://swapi.co/api/films/.../", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "film" + ] + } + ], + "data": { + "film": null + } +} +``` + +### Blog Quote: "You have to tell your callers what errors can happen" + +From the blog Joyent, ranked 1 for the keywords “Node.js logging” + + > We’ve talked about how to handle errors, but when you’re writing a new function, how do you deliver errors to the code that called your function? …If you don’t know what errors can happen or don’t know what they mean, then your program cannot be correct except by accident. So if you’re writing a new function, you have to tell your callers what errors can happen and what they mean… + +### Useful Tool: Swagger Online Documentation Creator + +![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/errorhandling/failfast.french.md b/sections/errorhandling/failfast.french.md new file mode 100644 index 000000000..79cfee70b --- /dev/null +++ b/sections/errorhandling/failfast.french.md @@ -0,0 +1,67 @@ +# Fail fast, validate arguments using a dedicated library + +### One Paragraph Explainer + +We all know how checking arguments and failing fast is important to avoid hidden bugs (see anti-pattern code example below). If not, read about explicit programming and defensive programming. In reality, we tend to avoid it due to the annoyance of coding it (e.g. think of validating hierarchical JSON object with fields like email and dates) – libraries like Joi and Validator turn this tedious task into a breeze. + +### Wikipedia: Defensive Programming + +Defensive programming is an approach to improve software and source code, in terms of General quality – reducing the number of software bugs and problems. Making the source code comprehensible – the source code should be readable and understandable so it is approved in a code audit. Making the software behave in a predictable manner despite unexpected inputs or user actions. + +### Code example: validating complex JSON input using ‘Joi’ + +```javascript +var memberSchema = Joi.object().keys({ + password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), + birthyear: Joi.number().integer().min(1900).max(2013), + email: Joi.string().email() +}); + +function addNewMember(newMember) { + // assertions come first + Joi.assert(newMember, memberSchema); //throws if validation fails + // other logic here +} +``` + + + +### Anti-pattern: no validation yields nasty bugs + +
+Javascript + +```javascript +// if the discount is positive let's then redirect the user to print his discount coupons +function redirectToPrintDiscount(httpResponse, member, discount) { + if (discount != 0) { + httpResponse.redirect(`/discountPrintView/${member.id}`); + } +} + +redirectToPrintDiscount(httpResponse, someMember); +// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen? +``` +
+ +
+Typescript + +```typescript +// if the discount is positive let's then redirect the user to print his discount coupons +function redirectToPrintDiscount(httpResponse: Response, member: Member, discount: number) { + if (discount != 0) { + httpResponse.redirect(`/discountPrintView/${member.id}`); + } +} + +redirectToPrintDiscount(httpResponse, someMember, -12); +// We passed a negative parameter discount, why the heck was the user redirected to the discount screen? +``` +
+ +### Blog Quote: "You should throw these errors immediately" + + From the blog: Joyent + + > A degenerate case is where someone calls an asynchronous function but doesn’t pass a callback. You should throw these errors immediately since the program is broken and the best chance of debugging it involves getting at least a stack trace and ideally a core file at the point of the error. To do this, we recommend validating the types of all arguments at the start of the function. diff --git a/sections/errorhandling/monitoring.french.md b/sections/errorhandling/monitoring.french.md new file mode 100644 index 000000000..81b71721d --- /dev/null +++ b/sections/errorhandling/monitoring.french.md @@ -0,0 +1,17 @@ +# Monitoring + +### One Paragraph Explainer + +> At the very basic level, monitoring means you can *easily identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the number of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw data to custom BI clients, Slack notifications and many others. + +Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, newrelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. AWS CloudWatch, Google StackDriver) will tell you immediately about the hardware metric but nothing about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack by default the hardware view. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to Elastic stack and configure some additional agent (e.g. Beat) to share hardware-related information to get the full picture. + +### Blog Quote: "We have a problem with promises" + + From the blog, pouchdb.com ranked 11 for the keywords “Node Promises” + + > … We recommend you to watch these signals for all of your services: Error Rate: Because errors are user facing and immediately affect your customers. +Response time: Because the latency directly affects your customers and business. +Throughput: The traffic helps you to understand the context of increased error rates and the latency too. +Saturation: It tells how “full” your service is. If the CPU usage is 90%, can your system handle more traffic? +… diff --git a/sections/errorhandling/operationalvsprogrammererror.french.md b/sections/errorhandling/operationalvsprogrammererror.french.md new file mode 100644 index 000000000..0e2f6b3e6 --- /dev/null +++ b/sections/errorhandling/operationalvsprogrammererror.french.md @@ -0,0 +1,85 @@ +# Distinguish operational vs programmer errors + +### One Paragraph Explainer + +Distinguishing the following two error types will minimize your app downtime and helps avoid crazy bugs: Operational errors refer to situations where you understand what happened and the impact of it – for example, a query to some HTTP service failed due to connection problem. On the other hand, programmer errors refer to cases where you have no idea why and sometimes where an error came from – it might be some code that tried to read an undefined value or DB connection pool that leaks memory. Operational errors are relatively easy to handle – usually logging the error is enough. Things become hairy when a programmer error pops up, the application might be in an inconsistent state and there’s nothing better you can do than to restart gracefully + +### Code Example – marking an error as operational (trusted) + +
+Javascript + +```javascript +// marking an error object as operational +const myError = new Error('How can I add new product when no value provided?'); +myError.isOperational = true; + +// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object") +class AppError { + constructor (commonType, description, isOperational) { + Error.call(this); + Error.captureStackTrace(this); + this.commonType = commonType; + this.description = description; + this.isOperational = isOperational; + } +}; + +throw new AppError(errorManagement.commonErrors.InvalidInput, 'Describe here what happened', true); + +``` +
+ +
+Typescript + +```typescript +// some centralized error factory (see other examples at the bullet "Use only the built-in Error object") +export class AppError extends Error { + public readonly commonType: string; + public readonly isOperational: boolean; + + constructor(commonType: string, description: string, isOperational: boolean) { + super(description); + + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + + this.commonType = commonType; + this.isOperational = isOperational; + + Error.captureStackTrace(this); + } +} + +// marking an error object as operational (true) +throw new AppError(errorManagement.commonErrors.InvalidInput, 'Describe here what happened', true); + +``` +
+ +### Blog Quote: "Programmer errors are bugs in the program" + +From the blog, Joyent ranked 1 for the keywords “Node.js error handling” + + > …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… + +### Blog Quote: "No safe way to leave without creating some undefined brittle state" + +From Node.js official documentation + + > …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker. + +### Blog Quote: "Otherwise you risk the state of your application" + +From the blog, debugable.com ranked 3 for the keywords “Node.js uncaught exception” + + > …So, unless you really know what you are doing, you should perform a graceful restart of your service after receiving an “uncaughtException” exception event. Otherwise, you risk the state of your application, or that of 3rd party libraries to become inconsistent, leading to all kinds of crazy bugs… + +### Blog Quote: "There are three schools of thoughts on error handling" + +From the blog: JS Recipes + +> …There are primarily three schools of thoughts on error handling: +1. Let the application crash and restart it. +2. Handle all possible errors and never crash. +3. A balanced approach between the two diff --git a/sections/errorhandling/shuttingtheprocess.french.md b/sections/errorhandling/shuttingtheprocess.french.md new file mode 100644 index 000000000..f708b5d12 --- /dev/null +++ b/sections/errorhandling/shuttingtheprocess.french.md @@ -0,0 +1,99 @@ +# Exit the process gracefully when a stranger comes to town + +### One Paragraph Explainer + +Somewhere within your code, an error handler object is responsible for deciding how to proceed when an error is thrown – if the error is trusted (i.e. operational error, see further explanation within best practice #3) then writing to log file might be enough. Things get hairy if the error is not familiar – this means that some component might be in a faulty state and all future requests are subject to failure. For example, assuming a singleton, stateful token issuer service that threw an exception and lost its state – from now it might behave unexpectedly and cause all requests to fail. Under this scenario, kill the process and use a ‘Restarter tool’ (like Forever, PM2, etc) to start over with a clean state. + +### Code example: deciding whether to crash + +
+Javascript + +```javascript +// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 +process.on('uncaughtException', (error) => { + errorManagement.handler.handleError(error); + if(!errorManagement.handler.isTrustedError(error)) + process.exit(1) +}); + +// centralized error handler encapsulates error-handling related logic +function errorHandler() { + this.handleError = (error) => { + return logger.logError(error) + .then(sendMailToAdminIfCritical) + .then(saveInOpsQueueIfCritical) + .then(determineIfOperationalError); + } + + this.isTrustedError = (error) => { + return error.isOperational; + } +} +``` +
+ +
+Typescript + +```typescript +// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 +process.on('uncaughtException', (error: Error) => { + errorManagement.handler.handleError(error); + if(!errorManagement.handler.isTrustedError(error)) + process.exit(1) +}); + +// centralized error object that derives from Node’s Error +export class AppError extends Error { + public readonly isOperational: boolean; + + constructor(description: string, isOperational: boolean) { + super(description); + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + this.isOperational = isOperational; + Error.captureStackTrace(this); + } +} + +// centralized error handler encapsulates error-handling related logic +class ErrorHandler { + public async handleError(err: Error): Promise { + await logger.logError(err); + await sendMailToAdminIfCritical(); + await saveInOpsQueueIfCritical(); + await determineIfOperationalError(); + }; + + public isTrustedError(error: Error) { + if (error instanceof AppError) { + return error.isOperational; + } + return false; + } +} + +export const handler = new ErrorHandler(); +``` +
+ +### Blog Quote: "The best way is to crash" + +From the blog Joyent + +> …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… + +### Blog Quote: "There are three schools of thoughts on error handling" + +From the blog: JS Recipes + +> …There are primarily three schools of thoughts on error handling: +1. Let the application crash and restart it. +2. Handle all possible errors and never crash. +3. A balanced approach between the two + +### Blog Quote: "No safe way to leave without creating some undefined brittle state" + +From Node.js official documentation + +> …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker. diff --git a/sections/errorhandling/testingerrorflows.french.md b/sections/errorhandling/testingerrorflows.french.md new file mode 100644 index 000000000..1e8ee43e0 --- /dev/null +++ b/sections/errorhandling/testingerrorflows.french.md @@ -0,0 +1,81 @@ +# Test error flows using your favorite test framework + +### One Paragraph Explainer + +Testing ‘happy’ paths is no better than testing failures. Good testing code coverage demands to test exceptional paths. Otherwise, there is no trust that exceptions are indeed handled correctly. Every unit testing framework, like [Mocha](https://mochajs.org/) & [Chai](http://chaijs.com/), supports exception testing (code examples below). If you find it tedious to test every inner function and exception you may settle with testing only REST API HTTP errors. + +### Code example: ensuring the right exception is thrown using Mocha & Chai + +
+Javascript + +```javascript +describe('Facebook chat', () => { + it('Notifies on new chat message', () => { + const chatService = new chatService(); + chatService.participants = getDisconnectedParticipants(); + expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); + }); +}); +``` +
+ +
+Typescript + +```typescript +describe('Facebook chat', () => { + it('Notifies on new chat message', () => { + const chatService = new chatService(); + chatService.participants = getDisconnectedParticipants(); + expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); + }); +}); +``` +
+ +### Code example: ensuring API returns the right HTTP error code + +
+Javascript + +```javascript +it('Creates new Facebook group', () => { + const invalidGroupInfo = {}; + return httpRequest({ + method: 'POST', + uri: 'facebook.com/api/groups', + resolveWithFullResponse: true, + body: invalidGroupInfo, + json: true + }).then((response) => { + expect.fail('if we were to execute the code in this block, no error was thrown in the operation above') + }).catch((response) => { + expect(400).to.equal(response.statusCode); + }); +}); +``` +
+ +
+Typescript + +```typescript +it('Creates new Facebook group', async () => { + let invalidGroupInfo = {}; + try { + const response = await httpRequest({ + method: 'POST', + uri: 'facebook.com/api/groups', + resolveWithFullResponse: true, + body: invalidGroupInfo, + json: true + }) + // if we were to execute the code in this block, no error was thrown in the operation above + expect.fail('The request should have failed') + } catch(response) { + expect(400).to.equal(response.statusCode); + } +}); +``` +
\ No newline at end of file diff --git a/sections/errorhandling/usematurelogger.french.md b/sections/errorhandling/usematurelogger.french.md new file mode 100644 index 000000000..4dcd088ca --- /dev/null +++ b/sections/errorhandling/usematurelogger.french.md @@ -0,0 +1,50 @@ +# Use a mature logger to increase errors visibility + +### One Paragraph Explainer + +We all love console.log but obviously, a reputable and persistent logger like [Winston][winston] (highly popular) or [Pino][pino] (the new kid in town which is focused on performance) is mandatory for serious projects. A set of practices and tools will help to reason about errors much quicker – (1) log frequently using different levels (debug, info, error), (2) when logging, provide contextual information as JSON objects, see example below. (3) Watch and filter logs using a log querying API (built-in in most loggers) or a log viewer software. (4) Expose and curate log statement for the operation team using operational intelligence tools like Splunk. + +[winston]: https://www.npmjs.com/package/winston +[pino]: https://www.npmjs.com/package/pino + +### Code Example – Winston Logger in action + +```javascript +// your centralized logger object +const logger = new winston.Logger({ + level: 'info', + transports: [ + new (winston.transports.Console)() + ] +}); + +// custom code somewhere using the logger +logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); +``` + +### Code Example – Querying the log folder (searching for entries) + +```javascript +const options = { + from: Date.now() - 24 * 60 * 60 * 1000, + until: new Date(), + limit: 10, + start: 0, + order: 'desc', + fields: ['message'] +}; + +// Find items logged between today and yesterday. +winston.query(options, (err, results) => { + // execute callback with results +}); +``` + +### Blog Quote: "Logger Requirements" + + From the blog Strong Loop + +> Lets identify a few requirements (for a logger): +1. Timestamp each log line. This one is pretty self-explanatory – you should be able to tell when each log entry occurred. +2. Logging format should be easily digestible by humans as well as machines. +3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time… diff --git a/sections/errorhandling/useonlythebuiltinerror.french.md b/sections/errorhandling/useonlythebuiltinerror.french.md new file mode 100644 index 000000000..bbe929322 --- /dev/null +++ b/sections/errorhandling/useonlythebuiltinerror.french.md @@ -0,0 +1,117 @@ +# Use only the built-in Error object + +### One Paragraph Explainer + +The permissive nature of JavaScript along with its variety of code-flow options (e.g. EventEmitter, Callbacks, Promises, etc) pushes to great variance in how developers raise errors – some use strings, other define their own custom types. Using Node.js built-in Error object helps to keep uniformity within your code and with 3rd party libraries, it also preserves significant information like the StackTrace. When raising the exception, it’s usually a good practice to fill it with additional contextual properties like the error name and the associated HTTP error code. To achieve this uniformity and practices, consider extending the Error object with additional properties, see code example below + +### Code Example – doing it right + +```javascript +// throwing an Error from typical function, whether sync or async +if(!productToAdd) + throw new Error('How can I add new product when no value provided?'); + +// 'throwing' an Error from EventEmitter +const myEmitter = new MyEmitter(); +myEmitter.emit('error', new Error('whoops!')); + +// 'throwing' an Error from a Promise +const addProduct = async (productToAdd) => { + try { + const existingProduct = await DAL.getProduct(productToAdd.id); + if (existingProduct !== null) { + throw new Error('Product already exists!'); + } + } catch (err) { + // ... + } +} +``` + +### Code example – Anti Pattern + +```javascript +// throwing a string lacks any stack trace information and other important data properties +if(!productToAdd) + throw ('How can I add new product when no value provided?'); +``` + +### Code example – doing it even better + +
+Javascript + +```javascript +// centralized error object that derives from Node’s Error +function AppError(name, httpCode, description, isOperational) { + Error.call(this); + Error.captureStackTrace(this); + this.name = name; + //...other properties assigned here +}; + +AppError.prototype = Object.create(Error.prototype); +AppError.prototype.constructor = AppError; + +module.exports.AppError = AppError; + +// client throwing an exception +if(user == null) + throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true) +``` +
+ +
+Typescript + +```typescript +// centralized error object that derives from Node’s Error +export class AppError extends Error { + public readonly name: string; + public readonly httpCode: HttpCode; + public readonly isOperational: boolean; + + constructor(name: string, httpCode: HttpCode, description: string, isOperational: boolean) { + super(description); + + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + + this.name = name; + this.httpCode = httpCode; + this.isOperational = isOperational; + + Error.captureStackTrace(this); + } +} + +// client throwing an exception +if(user == null) + throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true) +``` +
+ +*Explanation about the `Object.setPrototypeOf` in Typescript: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget* + +### Blog Quote: "I don’t see the value in having lots of different types" + +From the blog, Ben Nadel ranked 5 for the keywords “Node.js error object” + +>…”Personally, I don’t see the value in having lots of different types of error objects – JavaScript, as a language, doesn’t seem to cater to Constructor-based error-catching. As such, differentiating on an object property seems far easier than differentiating on a Constructor type… + +### Blog Quote: "A string is not an error" + +From the blog, devthought.com ranked 6 for the keywords “Node.js error object” + +> …passing a string instead of an error results in reduced interoperability between modules. It breaks contracts with APIs that might be performing `instanceof` Error checks, or that want to know more about the error. Error objects, as we’ll see, have very interesting properties in modern JavaScript engines besides holding the message passed to the constructor… + +### Blog Quote: "Inheriting from Error doesn’t add too much value" + +From the blog machadogj + +> …One problem that I have with the Error class is that is not so simple to extend. Of course, you can inherit the class and create your own Error classes like HttpError, DbError, etc. However, that takes time and doesn’t add too much value unless you are doing something with types. Sometimes, you just want to add a message and keep the inner error, and sometimes you might want to extend the error with parameters, and such… + +### Blog Quote: "All JavaScript and System errors raised by Node.js inherit from Error" + +From Node.js official documentation + +> …All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a “stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text description of the error. All errors generated by Node.js, including all System and JavaScript errors, will either be instances of or inherit from, the Error class… diff --git a/sections/performance/block-loop.french.md b/sections/performance/block-loop.french.md new file mode 100644 index 000000000..531de7fef --- /dev/null +++ b/sections/performance/block-loop.french.md @@ -0,0 +1,50 @@ +# Don't block the event loop + +

+ +Node handles the Event Loop mostly on a single thread rotating through multiple queues. Operations with high complexity, large json parsing, applying logic over huge arrays, unsafe regex queries, and large IO operations are some of the operations that can cause the Event Loop to stall. Avoid this off-loading CPU intensive tasks to a dedicated service (e.g. job server), or breaking long tasks into small steps then using the Worker Pool are some examples of how to avoid blocking the Event Loop. + +### Example: blocking the event loop +Let's take a look at an example from [Node Clinic](https://clinicjs.org/documentation/doctor/05-fixing-event-loop-problem). +```javascript +function sleep (ms) { + const future = Date.now() + ms + while (Date.now() < future); +} + +server.get('/', (req, res, next) => { + sleep(30) + res.send({}) + next() +}) +``` + +And when we benchmark this app, we start to see the latency caused by the long +while loop. + +### Run the benchmark +`clinic doctor --on-port 'autocannon localhost:$PORT' -- node slow-event-loop` + +### The results + +``` +─────────┬────────┬────────┬────────┬────────┬───────────┬──────────┬───────────┐ +│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ +├─────────┼────────┼────────┼────────┼────────┼───────────┼──────────┼───────────┤ +│ Latency │ 270 ms │ 300 ms │ 328 ms │ 331 ms │ 300.56 ms │ 38.55 ms │ 577.05 ms │ +└─────────┴────────┴────────┴────────┴────────┴───────────┴──────────┴───────────┘ +┌───────────┬─────────┬─────────┬─────────┬────────┬─────────┬───────┬─────────┐ +│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ +├───────────┼─────────┼─────────┼─────────┼────────┼─────────┼───────┼─────────┤ +│ Req/Sec │ 31 │ 31 │ 33 │ 34 │ 32.71 │ 1.01 │ 31 │ +├───────────┼─────────┼─────────┼─────────┼────────┼─────────┼───────┼─────────┤ +``` + +## Image of the Event Loop +![Event Loop](/assets/images/event-loop.png "Event Loop") + +>Here's a good rule of thumb for keeping your Node server speedy: Node is fast when the work associated with each client at any given time is "small". +>[Don't Block the Event Loop (or the Worker Pool) | Node.js](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) + +> Most people fail their first few NodeJS apps merely due to the lack of understanding of the concepts such as the Event Loop, Error handling and asynchrony +[Event Loop Best Practices — NodeJS Event Loop Part 5](https://jsblog.insiderattack.net/event-loop-best-practices-nodejs-event-loop-part-5-e29b2b50bfe2) diff --git a/sections/performance/nativeoverutil.french.md b/sections/performance/nativeoverutil.french.md new file mode 100644 index 000000000..09059cece --- /dev/null +++ b/sections/performance/nativeoverutil.french.md @@ -0,0 +1,70 @@ +# Prefer native JS methods over user-land utils like Lodash + + +

+ +### One Paragraph Explainer + +Sometimes, using native methods is better than requiring `lodash` or `underscore` because it will not lead in a performance boost and use more space than necessary. +The performance using native methods result in an [overall ~50% gain](https://github.com/Berkmann18/NativeVsUtils/blob/master/analysis.xlsx) which includes the following methods: `Array.concat`, `Array.fill`, `Array.filter`, `Array.map`, `(Array|String).indexOf`, `Object.find`, ... + + + + +

+ +### Example: benchmark comparison - Lodash vs V8 (Native) +The graph below shows the [mean of the benchmarks for a variety of Lodash methods](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.ods), this shows that Lodash methods take on average 146.23% more time to complete the same tasks as V8 methods. + +![meanDiag](../../assets/images/sampleMeanDiag.png) + +### Code Example – Benchmark test on `_.concat`/`Array.concat` +```javascript +const _ = require('lodash'); +const __ = require('underscore'); +const Suite = require('benchmark').Suite; +const opts = require('./utils'); //cf. https://github.com/Berkmann18/NativeVsUtils/blob/master/utils.js + +const concatSuite = new Suite('concat', opts); +const array = [0, 1, 2]; + +concatSuite.add('lodash', () => _.concat(array, 3, 4, 5)) + .add('underscore', () => __.concat(array, 3, 4, 5)) + .add('native', () => array.concat(3, 4, 5)) + .run({ 'async': true }); +``` + +Which returns this: + +![output](../../assets/images/concat-benchmark.png) + +You can find a bigger list of benchmarks [here](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.txt) or alternatively [run this](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.js) which would show the same but with colours. + +### Blog Quote: "You don't (may not) need Lodash/Underscore" + +From the [repo on this matter which focuses on Lodash and Underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore). + + > Lodash and Underscore are great modern JavaScript utility libraries, and they are widely used by Front-end developers. However, when you are targeting modern browsers, you may find out that there are many methods which are already supported natively thanks to ECMAScript5 [ES5] and ECMAScript2015 [ES6]. If you want your project to require fewer dependencies, and you know your target browser clearly, then you may not need Lodash/Underscore. + +### Example: Linting for non-native methods usage +There's an [ESLint plugin](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) which detects where you're using libraries but don't need to by warning you with suggestions (cf. example below).
+The way you set it up is by adding the `eslint-plugin-you-dont-need-lodash-underscore` plugin to your ESLint configuration file: +```json +{ + "extends": [ + "plugin:you-dont-need-lodash-underscore/compatible" + ] +} +``` + +### Example: detecting non-v8 util usage using a linter +Consider the file below: +```js +const _ = require('lodash'); +// ESLint will flag the line above with a suggestion +console.log(_.map([0, 1, 2, 4, 8, 16], x => `d${x}`)); +``` +Here's what ESLint would output when using the YDNLU plugin. +![output](../../assets/images/ydnlu.png) + +Of course, the example above doesn't seem realistic considering what actual codebases would have but you get the idea. diff --git a/sections/production/LTSrelease.french.md b/sections/production/LTSrelease.french.md new file mode 100644 index 000000000..1a3e0808a --- /dev/null +++ b/sections/production/LTSrelease.french.md @@ -0,0 +1,20 @@ +# Use an LTS release of Node.js in production + +### One Paragraph Explainer + +Ensure you are using an LTS(Long Term Support) version of Node.js in production to receive critical bug fixes, security updates and performance improvements. + +LTS versions of Node.js are supported for at least 18 months and are indicated by even version numbers (e.g. 4, 6, 8). They're best for production since the LTS release line is focussed on stability and security, whereas the 'Current' release line has a shorter lifespan and more frequent updates to the code. Changes to LTS versions are limited to bug fixes for stability, security updates, possible npm updates, documentation updates and certain performance improvements that can be demonstrated to not break existing applications. + +

+ +### Read on + +🔗 [Node.js release definitions](https://nodejs.org/en/about/releases/) + +🔗 [Node.js release schedule](https://github.com/nodejs/Release) + +🔗 [Essential Steps: Long Term Support for Node.js by Rod Vagg](https://medium.com/@nodesource/essential-steps-long-term-support-for-node-js-8ecf7514dbd) +> ...the schedule of incremental releases within each of these will be driven by the availability of bug fixes, security fixes, and other small but important changes. The focus will be on stability, but stability also includes minimizing the number of known bugs and staying on top of security concerns as they arise. + +

diff --git a/sections/production/apmproducts.french.md b/sections/production/apmproducts.french.md new file mode 100644 index 000000000..71c51794c --- /dev/null +++ b/sections/production/apmproducts.french.md @@ -0,0 +1,25 @@ +# Sure user experience with APM products + +

+ +### One Paragraph Explainer + +APM (application performance monitoring) refers to a family of products that aims to monitor application performance from end to end, also from the customer perspective. While traditional monitoring solutions focus on Exceptions and standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in the real world our app might create disappointed users without any code exceptions, for example, if some middleware service performed real slow. APM products measure the user experience from end to end, for example, given a system that encompasses frontend UI and multiple distributed services – some APM products can tell how fast a transaction that spans multiple tiers last. It can tell whether the user experience is solid and point to the problem. This attractive offering comes with a relatively high price tag hence it’s recommended for large-scale and complex products that require going beyond straightforward monitoring. + +

+ +### APM example – a commercial product that visualizes cross-service app performance + +![APM example](/assets/images/apm1.png "APM example") + +

+ +### APM example – a commercial product that emphasizes the user experience score + +![APM example](/assets/images/apm2.png "APM example") + +

+ +### APM example – a commercial product that highlights slow code paths + +![APM example](/assets/images/apm3.png "APM example") diff --git a/sections/production/assigntransactionid.french.md b/sections/production/assigntransactionid.french.md new file mode 100644 index 000000000..b398ed291 --- /dev/null +++ b/sections/production/assigntransactionid.french.md @@ -0,0 +1,39 @@ +# Assign ‘TransactionId’ to each log statement + +

+ +### One Paragraph Explainer + +A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious line or error, it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John” tried to buy something). This becomes even more critical and challenging in a microservice environment when a request/transaction might span across multiple computers. Address this by assigning a unique transaction identifier value to all the entries from the same request so when detecting one line one can copy the id and search for every line that has similar transaction Id. However, achieving this In Node is not straightforward as a single thread is used to serve all requests –consider using a library that that can group data on the request level – see code example on the next slide. When calling other microservice, pass the transaction Id using an HTTP header like “x-transaction-id” to keep the same context. + +

+ +### Code example: typical Express configuration + +```javascript +// when receiving a new request, start a new isolated context and set a transaction Id. The following example is using the npm library continuation-local-storage to isolate requests + +const { createNamespace } = require('continuation-local-storage'); +const session = createNamespace('my session'); + +router.get('/:id', (req, res, next) => { + session.set('transactionId', 'some unique GUID'); + someService.getById(req.params.id); + logger.info('Starting now to get something by Id'); +}); + +// Now any other service or components can have access to the contextual, per-request, data +class someService { + getById(id) { + logger.info('Starting to get something by Id'); + // other logic comes here + } +} + +// The logger can now append the transaction-id to each entry so that entries from the same request will have the same value +class logger { + info (message) { + console.log(`${message} ${session.get('transactionId')}`); + } +} +``` diff --git a/sections/production/bestateless.french.md b/sections/production/bestateless.french.md new file mode 100644 index 000000000..fc6f54de8 --- /dev/null +++ b/sections/production/bestateless.french.md @@ -0,0 +1,42 @@ +# Be stateless, kill your Servers almost every day + +

+ +### One Paragraph Explainer + +Have you ever encountered a severe production issue where one server was missing some piece of configuration or data? That is probably due to some unnecessary dependency on some local asset that is not part of the deployment. Many successful products treat servers like a phoenix bird – it dies and is reborn periodically without any damage. In other words, a server is just a piece of hardware that executes your code for some time and is replaced after that. +This approach + +- allows scaling by adding and removing servers dynamically without any side-effects. +- simplifies the maintenance as it frees our mind from evaluating each server state. + +

+ +### Code example: anti-patterns + +```javascript +// Typical mistake 1: saving uploaded files locally on a server +const multer = require('multer'); // express middleware for handling multipart uploads +const upload = multer({ dest: 'uploads/' }); + +app.post('/photos/upload', upload.array('photos', 12), (req, res, next) => {}); + +// Typical mistake 2: storing authentication sessions (passport) in a local file or memory +const FileStore = require('session-file-store')(session); +app.use(session({ + store: new FileStore(options), + secret: 'keyboard cat' +})); + +// Typical mistake 3: storing information on the global object +Global.someCacheLike.result = { somedata }; +``` + +

+ +### What Other Bloggers Say + +From the blog [Martin Fowler](https://martinfowler.com/bliki/PhoenixServer.html): +> ...One day I had this fantasy of starting a certification service for operations. The certification assessment would consist of a colleague and I turning up at the corporate data center and setting about critical production servers with a baseball bat, a chainsaw, and a water pistol. The assessment would be based on how long it would take for the operations team to get all the applications up and running again. This may be a daft fantasy, but there’s a nugget of wisdom here. While you should forego the baseball bats, it is a good idea to virtually burn down your servers at regular intervals. A server should be like a phoenix, regularly rising from the ashes... + +

diff --git a/sections/production/createmaintenanceendpoint.french.md b/sections/production/createmaintenanceendpoint.french.md new file mode 100644 index 000000000..c3a5fa0ef --- /dev/null +++ b/sections/production/createmaintenanceendpoint.french.md @@ -0,0 +1,45 @@ +# Create a maintenance endpoint + +

+ +### One Paragraph Explainer + +A maintenance endpoint is a highly secure HTTP API that is part of the app code and its purpose is to be used by the ops/production team to monitor and expose maintenance functionality. For example, it can return a heap dump (memory snapshot) of the process, report whether there are some memory leaks and even allow to execute REPL commands directly. This endpoint is needed where the conventional DevOps tools (monitoring products, logs, etc) fail to gather some specific type of information or you choose not to buy/install such tools. The golden rule is using professional and external tools for monitoring and maintaining the production, these are usually more robust and accurate. That said, there are likely to be cases where the generic tools will fail to extract information that is specific to Node or to your app – for example, should you wish to generate a memory snapshot at the moment GC completed a cycle – few npm libraries will be glad to perform this for you but popular monitoring tools will likely miss this functionality. It is important to keep this endpoint private and accessibly only by admins because it can become a target of a DDOS attack. + +

+ +### Code example: generating a heap dump via code + +```javascript +const heapdump = require('heapdump'); + +// Check if request is authorized +function isAuthorized(req) { + // ... +} + +router.get('/ops/heapdump', (req, res, next) => { + if (!isAuthorized(req)) { + return res.status(403).send('You are not authorized!'); + } + + logger.info('About to generate heapdump'); + + heapdump.writeSnapshot((err, filename) => { + console.log('heapdump file is ready to be sent to the caller', filename); + fs.readFile(filename, 'utf-8', (err, data) => { + res.end(data); + }); + }); +}); +``` + +

+ +### Recommended Resources + +[Getting your Node.js app production ready (Slides)](http://naugtur.pl/pres3/node2prod) + +▶ [Getting your Node.js app production ready (Video)](https://www.youtube.com/watch?v=lUsNne-_VIk) + +![Getting your Node.js app production ready](/assets/images/createmaintenanceendpoint1.png "Getting your Node.js app production ready") diff --git a/sections/production/delegatetoproxy.french.md b/sections/production/delegatetoproxy.french.md new file mode 100644 index 000000000..c7ed98d78 --- /dev/null +++ b/sections/production/delegatetoproxy.french.md @@ -0,0 +1,51 @@ +# Delegate anything possible (e.g. static content, gzip) to a reverse proxy + +

+ +### One Paragraph Explainer + +It’s very tempting to cargo-cult Express and use its rich middleware offering for networking related tasks like serving static files, gzip encoding, throttling requests, SSL termination, etc. This is a performance kill due to its single threaded model which will keep the CPU busy for long periods (Remember, Node’s execution model is optimized for short tasks or async IO related tasks). A better approach is to use a tool that expertise in networking tasks – the most popular are nginx and HAproxy which are also used by the biggest cloud vendors to lighten the incoming load on node.js processes. + +

+ +### Nginx Config Example – Using nginx to compress server responses + +```nginx +# configure gzip compression +gzip on; +gzip_comp_level 6; +gzip_vary on; + +# configure upstream +upstream myApplication { + server 127.0.0.1:3000; + server 127.0.0.1:3001; + keepalive 64; +} + +#defining web server +server { + # configure server with ssl and error pages + listen 80; + listen 443 ssl; + ssl_certificate /some/location/sillyfacesociety.com.bundle.crt; + error_page 502 /errors/502.html; + + # handling static content + location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { + root /usr/local/silly_face_society/node/public; + access_log off; + expires max; +} +``` + +

+ +### What Other Bloggers Say + +* From the blog [Mubaloo](http://mubaloo.com/best-practices-deploying-node-js-applications): +> …It’s very easy to fall into this trap – You see a package like Express and think “Awesome! Let’s get started” – you code away and you’ve got an application that does what you want. This is excellent and, to be honest, you’ve won a lot of the battle. However, you will lose the war if you upload your app to a server and have it listen on your HTTP port because you’ve forgotten a very crucial thing: Node is not a web server. **As soon as any volume of traffic starts to hit your application, you’ll notice that things start to go wrong: connections are dropped, assets stop being served or, at the very worst, your server crashes. What you’re doing is attempting to have Node deal with all of the complicated things that a proven web server does really well. Why reinvent the wheel?** +> **This is just for one request, for one image and bearing in mind this is the memory that your application could be used for important stuff like reading a database or handling complicated logic; why would you cripple your application for the sake of convenience?** + +* From the blog [Argteam](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> Although express.js has built-in static file handling through some connect middleware, you should never use it. **Nginx can do a much better job of handling static files and can prevent requests for non-dynamic content from clogging our node processes**… diff --git a/sections/production/detectvulnerabilities.french.md b/sections/production/detectvulnerabilities.french.md new file mode 100644 index 000000000..d67b9e819 --- /dev/null +++ b/sections/production/detectvulnerabilities.french.md @@ -0,0 +1,20 @@ +# Use tools that automatically detect vulnerable dependencies + +

+ +### One Paragraph Explainer + +Modern Node applications have tens and sometimes hundreds of dependencies. If any of the dependencies +you use has a known security vulnerability your app is vulnerable as well. +The following tools automatically check for known security vulnerabilities in your dependencies: + +- [npm audit](https://docs.npmjs.com/cli/audit) - npm audit +- [snyk](https://snyk.io/) - Continuously find & fix vulnerabilities in your dependencies + +

+ +### What Other Bloggers Say + +From the [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-one-security/) blog: + +> ...Using to manage your application’s dependencies is powerful and convenient. But the packages that you use may contain critical security vulnerabilities that could also affect your application. The security of your app is only as strong as the “weakest link” in your dependencies. Fortunately, there are two helpful tools you can use to ensure the third-party packages you use: nsp and requireSafe. These two tools do largely the same thing, so using both might be overkill, but “better safe than sorry” are words to live by when it comes to security... diff --git a/sections/production/frontendout.french.md b/sections/production/frontendout.french.md new file mode 100644 index 000000000..180bee4b2 --- /dev/null +++ b/sections/production/frontendout.french.md @@ -0,0 +1,45 @@ +# Get your frontend assets out of Node + +

+ +### One Paragraph Explainer + +In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the Node’s world is to use Express static middleware for streamlining static files to the client. BUT – Node is not a typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider using a reverse proxy (e.g. nginx, HAProxy), cloud storage or CDN (e.g. AWS S3, Azure Blob Storage, etc) that utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware like nginx embodies direct hooks between the file system and the network card and uses a multi-threaded approach to minimize intervention among multiple requests. + +Your optimal solution might wear one of the following forms: + +1. Using a reverse proxy – your static files will be located right next to your Node application, only requests to the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this approach, your Node app is responsible deploying the static files but not to serve them. Your frontend’s colleague will love this approach as it prevents cross-origin-requests from the frontend. + +2. Cloud storage – your static files will NOT be part of your Node app content, they will be uploaded to services like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach, your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling is drawn between Node and the Frontend which is anyway handled by different teams. + +

+ +### Configuration example: typical nginx configuration for serving static files + +```nginx +# configure gzip compression +gzip on; +keepalive 64; + +# defining web server +server { +listen 80; +listen 443 ssl; + +# handle static content +location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { +root /usr/local/silly_face_society/node/public; +access_log off; +expires max; +} +``` + +

+ +### What Other Bloggers Say + +From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/): + +>…In development, you can use [res.sendFile()](http://expressjs.com/4x/api.html#res.sendFile) to serve static files. But don’t do this in production, because this function has to read from the file system for every file request, so it will encounter significant latency and affect the overall performance of the app. Note that res.sendFile() is not implemented with the sendfile system call, which would make it far more efficient. Instead, use serve-static middleware (or something equivalent), that is optimized for serving files for Express apps. An even better option is to use a reverse proxy to serve static files; see Use a reverse proxy for more information… + +

diff --git a/sections/production/guardprocess.french.md b/sections/production/guardprocess.french.md new file mode 100644 index 000000000..ee76974bf --- /dev/null +++ b/sections/production/guardprocess.french.md @@ -0,0 +1,17 @@ +# Guard and restart your process upon failure (using the right tool) + +

+ +### One Paragraph Explainer + +At the base level, Node processes must be guarded and restarted upon failures. Simply put, for small apps and those who don’t use containers – tools like [PM2](https://www.npmjs.com/package/pm2-docker) are perfect as they bring simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux skills might use systemd and run Node as a service. Things get more interesting for apps that use Docker or any container technology since those are usually accompanied by cluster management and orchestration tools (e.g. [AWS ECS](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html), [Kubernetes](https://kubernetes.io/), etc) that deploy, monitor and heal containers. Having all those rich cluster management features including container restart, why mess up with other tools like PM2? There’s no bulletproof answer. There are good reasons to keep PM2 within containers (mostly its containers specific version [pm2-docker](https://www.npmjs.com/package/pm2-docker)) as the first guarding tier – it’s much faster to restart a process and provide Node-specific features like flagging to the code when the hosting container asks to gracefully restart. Other might choose to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to know the options is the important thing + +

+ +### What Other Bloggers Say + +* From the [Express Production Best Practices](https://expressjs.com/en/advanced/best-practice-performance.html): +> ... In development, you started your app simply from the command line with node server.js or something similar. **But doing this in production is a recipe for disaster. If the app crashes, it will be offline** until you restart it. To ensure your app restarts if it crashes, use a process manager. A process manager is a “container” for applications that facilitate deployment, provides high availability, and enables you to manage the application at runtime. + +* From the Medium blog post [Understanding Node Clustering](https://medium.com/@CodeAndBiscuits/understanding-nodejs-clustering-in-docker-land-64ce2306afef#.cssigr5z3): +> ... Understanding Node.js Clustering in Docker-Land “Docker containers are streamlined, lightweight virtual environments, designed to simplify processes to their bare minimum. Processes that manage and coordinate their own resources are no longer as valuable. **Instead, management stacks like Kubernetes, Mesos, and Cattle have popularized the concept that these resources should be managed infrastructure-wide**. CPU and memory resources are allocated by “schedulers”, and network resources are managed by stack-provided load balancers. diff --git a/sections/production/lockdependencies.french.md b/sections/production/lockdependencies.french.md new file mode 100644 index 000000000..fd0430d85 --- /dev/null +++ b/sections/production/lockdependencies.french.md @@ -0,0 +1,69 @@ +# Lock dependencies + +

+ +### One Paragraph Explainer + +Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production npm might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using npm config files and the argument ```–save-exact=true``` instructs npm to refer to the *exact* same version that was installed so the next time you run ```npm install``` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative and popular approach is using a `.shrinkwrap` file (easily generated using npm) that states exactly which packages and versions should be installed so no environment can get tempted to fetch newer versions than expected. + +* **Update:** as of npm 5, dependencies are locked automatically using .shrinkwrap. Yarn, an emerging package manager, also locks down dependencies by default. + +

+ +### Code example: .npmrc file that instructs npm to use exact versions + +```npmrc +// save this as .npmrc file on the project directory +save-exact:true +``` + +

+ +### Code example: shrinkwrap.json file that distills the exact dependency tree + +```json +{ + "name": "A", + "dependencies": { + "B": { + "version": "0.0.1", + "dependencies": { + "C": { + "version": "0.1.0" + } + } + } + } +} +``` + +

+ +### Code example: npm 5 dependencies lock file – package.json + +```json +{ + "name": "package-name", + "version": "1.0.0", + "lockfileVersion": 1, + "dependencies": { + "cacache": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", + "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" + }, + "duplexify": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", + "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" + } + } + } + } +} +``` diff --git a/sections/production/logrouting.french.md b/sections/production/logrouting.french.md new file mode 100644 index 000000000..9fac3f52a --- /dev/null +++ b/sections/production/logrouting.french.md @@ -0,0 +1,87 @@ +# Your application code should not handle log routing + +

+ +### One Paragraph Explainer + +Application code should not handle log routing, but instead should use a logger utility to write to `stdout/stderr`. “Log routing” means picking up and pushing logs to a some other location than your application or application process, for example, writing the logs to a file, database, etc. The reason for this is mostly two-fold: 1) separation of concerns and 2) [12-Factor best practices for modern applications](https://12factor.net/logs). + +We often think of "separation of concerns" in terms of pieces of code between services and between services themselves, but this applies to the more “infrastructural” components as well. Your application code should not handle something that should be handled by infrastructure/the execution environment (most often these days, containers). What happens if you define the log locations in your application, but later you need to change that location? That results in a code change and deployment. When working with container-based/cloud-based platforms, containers can spin up and shut down when scaling to performance demands, so we can't be sure where a logfile will end up. The execution environment (container) should decide where the log files get routed to instead. The application should just log what it needs to to `stdout` / `stderr`, and the execution environment should be configured to pick up the log stream from there and route it to where it needs to go. Also, those on the team who need to specify and/or change the log destinations are often not application developers but are part of DevOps, and they might not have familiarity with the application code. This prevents them from easily making changes. + +

+ +### Code Example – Anti-pattern: Log routing tightly coupled to application + +```javascript +const { createLogger, transports, winston } = require('winston'); +/** + * Requiring `winston-mongodb` will expose + * `winston.transports.MongoDB` + */ +require('winston-mongodb'); + +// log to two different files, which the application now must be concerned with +const logger = createLogger({ + transports: [ + new transports.File({ filename: 'combined.log' }), + ], + exceptionHandlers: [ + new transports.File({ filename: 'exceptions.log' }) + ] +}); + +// log to MongoDB, which the application now must be concerned with +winston.add(winston.transports.MongoDB, options); +``` +Doing it this way, the application now handles both application/business logic AND log routing logic! + +

+ +### Code Example – Better log handling + Docker example +In the application: +```javascript +const logger = new winston.Logger({ + level: 'info', + transports: [ + new (winston.transports.Console)() + ] +}); + +logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); +``` +Then, in the docker container `daemon.json`: +```json5 +{ + "log-driver": "splunk", // just using Splunk as an example, it could be another storage type + "log-opts": { + "splunk-token": "", + "splunk-url": "", + //... + } +} +``` +So this example ends up looking like `log -> stdout -> Docker container -> Splunk` + +

+ +### Blog Quote: "O'Reilly" + +From the [O'Reilly blog](https://www.oreilly.com/ideas/a-cloud-native-approach-to-logs), + > When you have a fixed number of instances on a fixed number of servers, storing logs on disk seems to make sense. However, when your application can dynamically go from 1 running instance to 100, and you have no idea where those instances are running, you need your cloud provider to deal with aggregating those logs on your behalf. + +

+ +### Quote: "12-Factor" + +From the [12-Factor best practices for logging](https://12factor.net/logs), + > A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout. + + > In staging or production deploys, each process’ stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival. These archival destinations are not visible to or configurable by the app, and instead are completely managed by the execution environment. + +

+ + ### Example: Architecture overview using Docker and Splunk as an example + +![alt text](/assets/images/logging-overview.png "Log routing overview") + +

diff --git a/sections/production/measurememory.french.md b/sections/production/measurememory.french.md new file mode 100644 index 000000000..19c700755 --- /dev/null +++ b/sections/production/measurememory.french.md @@ -0,0 +1,25 @@ +# Measure and guard the memory usage + +

+ +### One Paragraph Explainer + +In a perfect world, a web developer shouldn’t deal with memory leaks. In reality, memory issues are a known Node’s gotcha one must be aware of. Above all, memory usage must be monitored constantly. In the development and small production sites, you may gauge manually using Linux commands or npm tools and libraries like node-inspector and memwatch. The main drawback of this manual activities is that they require a human being actively monitoring – for serious production sites, it’s absolutely vital to use robust monitoring tools e.g. (AWS CloudWatch, DataDog or any similar proactive system) that alerts when a leak happens. There are also few development guidelines to prevent leaks: avoid storing data on the global level, use streams for data with dynamic size, limit variables scope using let and const. + +

+ +### What Other Bloggers Say + +* From the blog [Dyntrace](http://apmblog.dynatrace.com/): +> ... ”As we already learned, in Node.js JavaScript is compiled to native code by V8. The resulting native data structures don’t have much to do with their original representation and are solely managed by V8. This means that we cannot actively allocate or deallocate memory in JavaScript. V8 uses a well-known mechanism called garbage collection to address this problem.” + +* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> ... “Although this example leads to obvious results the process is always the same: +Create heap dumps with some time and a fair amount of memory allocation in between +Compare a few dumps to find out what’s growing” + +* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> ... “fault, Node.js will try to use about 1.5GBs of memory, which has to be capped when running on systems with less memory. This is the expected behavior as garbage collection is a very costly operation. +The solution for it was adding an extra parameter to the Node.js process: +node –max_old_space_size=400 server.js –production ” +“Why is garbage collection expensive? The V8 JavaScript engine employs a stop-the-world garbage collector mechanism. In practice, it means that the program stops execution while garbage collection is in progress.” diff --git a/sections/production/monitoring.french.md b/sections/production/monitoring.french.md new file mode 100644 index 000000000..68f42cbd9 --- /dev/null +++ b/sections/production/monitoring.french.md @@ -0,0 +1,39 @@ +# Monitoring! + +

+ +### One Paragraph Explainer + +At the very basic level, monitoring means you can *easily* identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the number of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw data to custom BI clients, Slack notifications and many others. + +Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, NewRelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), [Google StackDriver](https://cloud.google.com/stackdriver/)) will tell you immediately about the hardware metrics but not about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to [Elastic stack](https://www.elastic.co/products) and configure some additional agent (e.g. [Beat](https://www.elastic.co/products)) to share hardware-related information to get the full picture. + +

+ +### Monitoring example: AWS cloudwatch default dashboard. Hard to extract in-app metrics + +![AWS cloudwatch default dashboard. Hard to extract in-app metrics](/assets/images/monitoring1.png) + +

+ +### Monitoring example: StackDriver default dashboard. Hard to extract in-app metrics + +![StackDriver default dashboard. Hard to extract in-app metrics](/assets/images/monitoring2.jpg) + +

+ +### Monitoring example: Grafana as the UI layer that visualizes raw data + +![Grafana as the UI layer that visualizes raw data](/assets/images/monitoring3.png) + +

+ +### What Other Bloggers Say + +From the blog [Rising Stack](http://mubaloo.com/best-practices-deploying-node-js-applications/): + +> …We recommend you to watch these signals for all of your services: +> Error Rate: Because errors are user facing and immediately affect your customers. +> Response time: Because the latency directly affects your customers and business. +> Throughput: The traffic helps you to understand the context of increased error rates and the latency too. +> Saturation: It tells how “full” your service is. If the CPU usage is 90%, can your system handle more traffic? … diff --git a/sections/production/productioncode.french.md b/sections/production/productioncode.french.md new file mode 100644 index 000000000..4871ad396 --- /dev/null +++ b/sections/production/productioncode.french.md @@ -0,0 +1,16 @@ +# Make your code production-ready + +

+ +### One Paragraph Explainer + +Following is a list of development tips that greatly affect the production maintenance and stability: + +* The twelve-factor guide – Get familiar with the [Twelve factors](https://12factor.net/) guide +* Be stateless – Save no data locally on a specific web server (see separate bullet – ‘Be Stateless’) +* Cache – Utilize cache heavily, yet never fail because of cache mismatch +* Test memory – gauge memory usage and leaks as part your development flow, tools such as ‘memwatch’ can greatly facilitate this task +* Name functions – Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will provide memory usage per method name +* Use CI tools – Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference errors and undefined variables. Use –trace-sync-io to identify code that uses synchronous APIs (instead of the async version) +* Log wisely – Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such as Elastic can search upon those properties (see separate bullet – ‘Increase visibility using smart logs’). Also, include transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate bullet – ‘Include Transaction-ID’) +* Error management – Error handling is the Achilles’ heel of Node.js production sites – many Node processes are crashing because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy is absolutely critical, read here my [error handling best practices](http://goldbergyoni.com/checklist-best-practices-of-node-js-error-handling/) diff --git a/sections/production/setnodeenv.french.md b/sections/production/setnodeenv.french.md new file mode 100644 index 000000000..3f537f7bc --- /dev/null +++ b/sections/production/setnodeenv.french.md @@ -0,0 +1,34 @@ +# Set NODE_ENV = production + +

+ +### One Paragraph Explainer + +Process environment variables is a set of key-value pairs made available to any running program, usually for configuration purposes. Though any variables can be used, Node encourages the convention of using a variable called NODE_ENV to flag whether we’re in production right now. This determination allows components to provide better diagnostics during development, for example by disabling caching or emitting verbose log statements. Any modern deployment tool – Chef, Puppet, CloudFormation, others – support setting environment variables during deployment + +

+ +### Code example: Setting and reading the NODE_ENV environment variable + +```shell script +// Setting environment variables in bash before starting the node process +$ NODE_ENV=development +$ node +``` + +```javascript +// Reading the environment variable using code +if (process.env.NODE_ENV === 'production') + useCaching = true; +``` + +

+ +### What Other Bloggers Say + +From the blog [dynatrace](https://www.dynatrace.com/blog/the-drastic-effects-of-omitting-node_env-in-your-express-js-applications/): +> ...In Node.js there is a convention to use a variable called NODE_ENV to set the current mode. We see that it, in fact, reads NODE_ENV and defaults to ‘development’ if it isn’t set. We clearly see that by setting NODE_ENV to production the number of requests Node.js can handle jumps by around two-thirds while the CPU usage even drops slightly. *Let me emphasize this: Setting NODE_ENV to production makes your application 3 times faster!* + +![NODE_ENV=production](/assets/images/setnodeenv1.png "NODE_ENV=production") + +

diff --git a/sections/production/smartlogging.french.md b/sections/production/smartlogging.french.md new file mode 100644 index 000000000..5aab52d1f --- /dev/null +++ b/sections/production/smartlogging.french.md @@ -0,0 +1,40 @@ +# Make your app transparent using smart logs + +

+ +### One Paragraph Explainer + +Since you print out log statements anyway and you're obviously in a need of some interface that wraps up production information where you can trace errors and core metrics (e.g. how many errors happen every hour and which is your slowest API end-point) why not invest some moderate effort in a robust logging framework that will tick all boxes? Achieving that requires a thoughtful decision on three steps: + +**1. smart logging** – at the bare minimum you need to use a reputable logging library like [Winston](https://github.com/winstonjs/winston), [Bunyan](https://github.com/trentm/node-bunyan) and write meaningful information at each transaction start and end. Consider to also format log statements as JSON and provide all the contextual properties (e.g. user id, operation type, etc) so that the operations team can act on those fields. Include also a unique transaction ID at each log line, for more information refer to the bullet below “Write transaction-id to log”. One last point to consider is also including an agent that logs the system resource like memory and CPU like Elastic Beat. + +**2. smart aggregation** – once you have comprehensive information on your servers file system, it’s time to periodically push these to a system that aggregates, facilities and visualizes this data. The Elastic stack, for example, is a popular and free choice that offers all the components to aggregate and visualize data. Many commercial products provide similar functionality only they greatly cut down the setup time and require no hosting. + +**3. smart visualization** – now the information is aggregated and searchable, one can be satisfied only with the power of easily searching the logs but this can go much further without coding or spending much effort. We can now show important operational metrics like error rate, average CPU throughout the day, how many new users opted-in in the last hour and any other metric that helps to govern and improve our app + +

+ +### Visualization Example: Kibana (part of the Elastic stack) facilitates advanced searching on log content + +![Kibana facilitates advanced searching on log content](/assets/images/smartlogging1.png "Kibana facilitates advanced searching on log content") + +

+ +### Visualization Example: Kibana (part of the Elastic stack) visualizes data based on logs + +![Kibana visualizes data based on logs](/assets/images/smartlogging2.jpg "Kibana visualizes data based on logs") + +

+ +### Blog Quote: Logger Requirements + +From the blog [Strong Loop](https://strongloop.com/strongblog/compare-node-js-logging-winston-bunyan/): + +> Lets identify a few requirements (for a logger): +> 1. Timestamp each log line. This one is pretty self-explanatory – you should be able to tell when each log entry occurred. +> 2. Logging format should be easily digestible by humans as well as machines. +> 3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time… + +

+ +

diff --git a/sections/production/utilizecpu.french.md b/sections/production/utilizecpu.french.md new file mode 100644 index 000000000..c99a081b0 --- /dev/null +++ b/sections/production/utilizecpu.french.md @@ -0,0 +1,26 @@ +# Utilize all CPU cores + +

+ +### One Paragraph Explainer + +It might not come as a surprise that in its basic form, Node runs over a single thread=single process=single CPU. Paying for beefy hardware with 4 or 8 CPU and utilizing only one sounds crazy, right? The quickest solution which fits medium sized apps is using Node’s Cluster module which in 10 lines of code spawns a process for each logical core and route requests between the processes in a round-robin style. Even better, use PM2 which sugarcoats the clustering module with a simple interface and cool monitoring UI. While this solution works well for traditional applications, it might fall short for applications that require top-notch performance and robust DevOps flow. For those advanced use cases, consider replicating the NODE process using custom deployment script and balancing using a specialized tool such as nginx or use a container engine such as AWS ECS or Kubernetees that have advanced features for deployment and replication of processes. + +

+ +### Comparison: Balancing using Node’s cluster vs nginx + +![Balancing using Node’s cluster vs nginx](/assets/images/utilizecpucores1.png "Balancing using Node’s cluster vs nginx") + +

+ +### What Other Bloggers Say + +* From the [Node.js documentation](https://nodejs.org/api/cluster.html#cluster_how_it_works): +> ... The second approach, Node clusters, should, in theory, give the best performance. In practice, however, distribution tends to be very unbalanced due to operating system scheduler vagaries. Loads have been observed where over 70% of all connections ended up in just two processes, out of a total of eight ... + +* From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/): +> ... Clustering is made possible with Node’s cluster module. This enables a master process to spawn worker processes and distribute incoming connections among the workers. However, rather than using this module directly, it’s far better to use one of the many tools out there that do it for you automatically; for example node-pm or cluster-service ... + +* From the Medium post [Node.js process load balance performance: comparing cluster module, iptables, and Nginx](https://medium.com/@fermads/node-js-process-load-balancing-comparing-cluster-iptables-and-nginx-6746aaf38272) +> ... Node cluster is simple to implement and configure, things are kept inside Node’s realm without depending on other software. Just remember your master process will work almost as much as your worker processes and with a little less request rate than the other solutions ... diff --git a/sections/projectstructre/breakintcomponents.french.md b/sections/projectstructre/breakintcomponents.french.md new file mode 100644 index 000000000..c28a0f10f --- /dev/null +++ b/sections/projectstructre/breakintcomponents.french.md @@ -0,0 +1,37 @@ +# Structure your solution by components + +

+ +### One Paragraph Explainer + +For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows. + +

+ +### Blog Quote: "Scaling requires scaling of the entire application" + + From the blog MartinFowler.com + +> Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource. + +

+ +### Blog Quote: "So what does the architecture of your application scream?" + + From the blog [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html) + +> ...if you were looking at the architecture of a library, you’d likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.
+ +So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?. + +

+ +### Good: Structure your solution by self-contained components + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") + +

+ +### Bad: Group your files by technical role + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/configguide.french.md b/sections/projectstructre/configguide.french.md new file mode 100644 index 000000000..bb8c8ed8f --- /dev/null +++ b/sections/projectstructre/configguide.french.md @@ -0,0 +1,43 @@ +# Use environment aware, secure and hierarchical config + +

+ +### One Paragraph Explainer + +When dealing with configuration data, many things can just annoy and slow down: + +1. setting all the keys using process environment variables becomes very tedious when in need to inject 100 keys (instead of just committing those in a config file), however when dealing with files only the DevOps admins cannot alter the behavior without changing the code. A reliable config solution must combine both configuration files + overrides from the process variables + +2. when specifying all keys in a flat JSON, it becomes frustrating to find and modify entries when the list grows bigger. A hierarchical JSON file that is grouped into sections can overcome this issue + few config libraries allow to store the configuration in multiple files and take care to union all at runtime. See example below + +3. storing sensitive information like DB password is obviously not recommended but no quick and handy solution exists for this challenge. Some configuration libraries allow to encrypt files, others encrypt those entries during GIT commits or simply don't store real values for those entries and specify the actual value during deployment via environment variables. + +4. some advanced configuration scenarios demand to inject configuration values via command line (vargs) or sync configuration info via a centralized cache like Redis so multiple servers will use the same configuration data. + +5. the application should fail as fast as possible and provide the immediate feedback if the required environment variables are not present at start-up, this can be achieved by using [convict](https://www.npmjs.com/package/convict) to validate the configuration. + +Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) which tick many of these requirements. + +

+ +### Code Example – hierarchical config helps to find entries and maintain huge config files + +```json5 +{ + // Customer module configs + "Customer": { + "dbConfig": { + "host": "localhost", + "port": 5984, + "dbName": "customers" + }, + "credit": { + "initialLimit": 100, + // Set low for development + "initialDays": 1 + } + } +} +``` + +

diff --git a/sections/projectstructre/createlayers.french.md b/sections/projectstructre/createlayers.french.md new file mode 100644 index 000000000..cdadfd85d --- /dev/null +++ b/sections/projectstructre/createlayers.french.md @@ -0,0 +1,13 @@ +# Layer your app, keep Express within its boundaries + +

+ + ### Separate component code into layers: web, services, and DAL + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") + +

+ +### 1 min explainer: The downside of mixing layers + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/separateexpress.french.md b/sections/projectstructre/separateexpress.french.md new file mode 100644 index 000000000..3740dd311 --- /dev/null +++ b/sections/projectstructre/separateexpress.french.md @@ -0,0 +1,98 @@ +# Separate Express 'app' and 'server' + +

+ +### One Paragraph Explainer + +The latest Express generator comes with a great practice that is worth to keep - the API declaration is separated from the network related configuration (port, protocol, etc). This allows testing the API in-process, without performing network calls, with all the benefits that it brings to the table: fast testing execution and getting coverage metrics of the code. It also allows deploying the same API under flexible and different network conditions. Bonus: better separation of concerns and cleaner code + +

+ +### Code example: API declaration, should reside in app.js/app.ts + +```javascript +const app = express(); +app.use(bodyParser.json()); +app.use('/api/events', events.API); +app.use('/api/forms', forms); +``` + +### Code example: Server network declaration, should reside in /bin/www + +
+Javascript + +```javascript +const app = require('../app'); +const http = require('http'); + +// Get port from environment and store in Express. +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +// Create HTTP server. +const server = http.createServer(app); +``` +
+ +
+Typescript + +```typescript +import app from '../app'; +import http from 'http'; + +// Get port from environment and store in Express. +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +// Create HTTP server. +const server = http.createServer(app); +``` +
+ +### Example: test your API in-process using supertest (popular testing package) + +
+Javascript + +```javascript +const app = express(); + +app.get('/user', (req, res) => { + res.status(200).json({ name: 'tobi' }); +}); + +request(app) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end((err, res) => { + if (err) throw err; + }); +``` +
+ + +
+Typescript + +```typescript +const app = express(); + +app.get('/user', (req: Request, res: Response) => { + res.status(200).json({ name: 'tobi' }); +}); + +request(app) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end((err: Error) => { + if (err) throw err; + }); + +``` +
\ No newline at end of file diff --git a/sections/projectstructre/thincomponents.french.md b/sections/projectstructre/thincomponents.french.md new file mode 100644 index 000000000..750d31386 --- /dev/null +++ b/sections/projectstructre/thincomponents.french.md @@ -0,0 +1,27 @@ +# Structure your solution by components + +

+ +### One Paragraph Explainer + +For medium sized apps and above, monoliths are really bad - one big software with many dependencies is just hard to reason about and often leads to code spaghetti. Even those smart architects who are skilled to tame the beast and 'modularize' it - spend great mental effort on design and each change requires to carefully evaluate the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc) so that it's very easy to reason about it. Some may call this 'microservices' architecture - it's important to understand that microservices are not a spec which you must follow rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependencies hell and pave the way to full-blown microservices in the future once your app grows + +

+ +### Blog Quote: "Scaling requires scaling of the entire application" + + From the blog MartinFowler.com + + > Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource. + +

+ +### Good: Structure your solution by self-contained components + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") + +

+ +### Bad: Group your files by technical role + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/wraputilities.french.md b/sections/projectstructre/wraputilities.french.md new file mode 100644 index 000000000..23cca38ca --- /dev/null +++ b/sections/projectstructre/wraputilities.french.md @@ -0,0 +1,13 @@ +# Wrap common utilities as npm packages + +

+ +### One Paragraph Explainer + +Once you start growing and have different components on different servers which consumes similar utilities, you should start managing the dependencies - how can you keep 1 copy of your utility code and let multiple consumer components use and deploy it? well, there is a tool for that, it's called npm... Start by wrapping 3rd party utility packages with your own code to make it easily replaceable in the future and publish your own code as private npm package. Now, all your code base can import that code and benefit free dependency management tool. It's possible to publish npm packages for your own private use without sharing it publicly using [private modules](https://docs.npmjs.com/private-modules/intro), [private registry](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html) or [local npm packages](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc) + +

+ +### Sharing your own common utilities across environments and components + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/security/avoid_publishing_secrets.french.md b/sections/security/avoid_publishing_secrets.french.md new file mode 100644 index 000000000..934325bb5 --- /dev/null +++ b/sections/security/avoid_publishing_secrets.french.md @@ -0,0 +1,44 @@ +# Avoid publishing secrets to the npm registry + +### One Paragraph Explainer +Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. + +To gain a view of what npm publish will really publish to the registry, the `--dry-run` flag can be added the npm publish command to provide a verbose view of the tarbell package created. + +It is important to note that if a project is utilising both `.npmignore` and `.gitignore` files, everything which isn't in `.npmignore` is published to the registry(i.e. the `.npmignore` file overrides the `.gitignore`). This condition is a common source of confusion and is a problem that can lead to leaking secrets. Developers may end up updating the `.gitignore` file, but forget to update `.npmignore` as well, which can lead to a potentially sensitive file not being pushed to source control, but still being included in the npm package. + +### Code example +Example .npmignore file +``` +#tests +test +coverage + +#build tools +.travis.yml +.jenkins.yml + +#environment +.env +.config + +``` + +Example use of files array in package.json + +``` +{ + "files" : [ + "dist/moment.js", + "dist/moment.min.js" + ] +} +``` + +### What other bloggers say + +From the blog by [Liran Tal & Juan Picado at Snyk](https://snyk.io/blog/ten-npm-security-best-practices/): +> ... Another good practice to adopt is making use of the files property in package.json, which works as a whitelist and specifies the array of files to be included in the package that is to be created and installed (while the ignore file functions as a blacklist). The files property and an ignore file can both be used together to determine which files should explicitly be included, as well as excluded, from the package. When using both, the former the files property in package.json takes precedence over the ignore file. + +From the [npm blog](https://blog.npmjs.org/post/165769683050/publishing-what-you-mean-to-publish) +> ... When you run npm publish, npm bundles up all the files in the current directory. It makes a few decisions for you about what to include and what to ignore. To make these decisions, it uses the contents of several files in your project directory. These files include .gitignore, .npmignore, and the files array in the package.json. It also always includes certain files and ignores others. \ No newline at end of file diff --git a/sections/security/avoideval.french.md b/sections/security/avoideval.french.md new file mode 100644 index 000000000..5ccb63863 --- /dev/null +++ b/sections/security/avoideval.french.md @@ -0,0 +1,23 @@ +# Avoid JS eval statements + +### One Paragraph Explainer + +`eval()`, `setTimeout()`, `setInterval()`, and `new Function()` are global functions, often used in Node.js, which accept a string parameter representing a JavaScript expression, statement, or sequence of statements. The security concern of using these functions is the possibility that untrusted user input might find its way into code execution leading to server compromise, as evaluating user code essentially allows an attacker to perform any actions that you can. It is suggested to refactor code to not rely on the usage of these functions where user input could be passed to the function and executed. + +### Code example + +```javascript +// example of malicious code which an attacker was able to input +const userInput = "require('child_process').spawn('rm', ['-rf', '/'])"; + +// malicious code executed +eval(userInput); +``` + +### What other bloggers say + +From the Essential Node.js Security book by [Liran Tal](https://leanpub.com/nodejssecurity): +> The eval() function is perhaps of the most frowned upon JavaScript pieces from a security +perspective. It parses a JavaScript string as text, and executes it as if it were a JavaScript code. +Mixing that with untrusted user input that might find it’s way to eval() is a recipe for disaster that +can end up with server compromise. diff --git a/sections/security/bcryptpasswords.french.md b/sections/security/bcryptpasswords.french.md new file mode 100644 index 000000000..02a71d4f9 --- /dev/null +++ b/sections/security/bcryptpasswords.french.md @@ -0,0 +1,32 @@ +# Avoid using the Node.js Crypto library for passwords, use Bcrypt + +### One Paragraph Explainer + +When storing user passwords, using an adaptive hashing algorithm such as bcrypt, offered by the [bcrypt npm module](https://www.npmjs.com/package/bcrypt) is recommended as opposed to using the native Node.js crypto module. `Math.random()` should also never be used as part of any password or token generation due to its predictability. + +The `bcrypt` module or similar should be used as opposed to the JavaScript implementation, as when using `bcrypt`, a number of 'rounds' can be specified in order to provide a secure hash. This sets the work factor or the number of 'rounds' the data is processed for, and more hashing rounds leads to more secure hash (although this at the cost of CPU time). The introduction of hashing rounds means that the brute force factor is significantly reduced, as password crackers are slowed down increasing the time required to generate one attempt. + +### Code example + +```javascript +try { +// asynchronously generate a secure password using 10 hashing rounds + const hash = await bcrypt.hash('myPassword', 10); + // Store secure hash in user record + + // compare a provided password input with saved hash + const match = await bcrypt.compare('somePassword', hash); + if (match) { + // Passwords match + } else { + // Passwords don't match + } +} catch { + logger.error('could not hash password.') +} +``` + +### What other bloggers say + +From the blog by [Max McCarty](https://dzone.com/articles/nodejs-and-password-storage-with-bcrypt): +> ... it’s not just using the right hashing algorithm. I’ve talked extensively about how the right tool also includes the necessary ingredient of “time” as part of the password hashing algorithm and what it means for the attacker who’s trying to crack passwords through brute-force. diff --git a/sections/security/childprocesses.french.md b/sections/security/childprocesses.french.md new file mode 100644 index 000000000..16fddc7d0 --- /dev/null +++ b/sections/security/childprocesses.french.md @@ -0,0 +1,31 @@ +# Be cautious when working with child processes + +### One Paragraph Explainer + +As great as child processes are, they should be used with caution. Passing in user input must be sanitized, if not avoided at all. +The dangers of unsanitized input executing system-level logic are unlimited, reaching from remote code execution to the exposure of +sensitive system data and even data loss. A check list of preparations could look like this + +- avoid user input in every case, otherwise validate and sanitize it +- limit the privileges of the parent and child processes using user/group identities +- run your process inside of an isolated environment to prevent unwanted side-effects if the other preparations fail + +### Code example: Dangers of unsanitized child process executions + +```javascript +const { exec } = require('child_process'); + +... + +// as an example, take a script that takes two arguments, one of them is unsanitized user input +exec('"/path/to/test file/someScript.sh" --someOption ' + input); + +// -> imagine what could happen if the user simply enters something like '&& rm -rf --no-preserve-root /' +// you'd be in for an unwanted surprise +``` + +### Additional resources + +From the Node.js child process [documentation](https://nodejs.org/dist/latest-v8.x/docs/api/child_process.html#child_process_child_process_exec_command_options_callback): + +> Never pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution. diff --git a/sections/security/commonsecuritybestpractices.french.md b/sections/security/commonsecuritybestpractices.french.md new file mode 100644 index 000000000..1f44271a2 --- /dev/null +++ b/sections/security/commonsecuritybestpractices.french.md @@ -0,0 +1,99 @@ +[✔]: ../../assets/images/checkbox-small-blue.png + +# Common Node.js security best practices + +The common security guidelines section contains best practices that are standardized in many frameworks and conventions, running an application with SSL/TLS, for example, should be a common guideline and convention followed in every setup to achieve great security benefits. + +## ![✔] Use SSL/TLS to encrypt the client-server connection + +**TL;DR:** In the times of [free SSL/TLS certificates](https://letsencrypt.org/) and easy configuration of those, you do no longer have to weigh advantages and disadvantages of using a secure server because the advantages such as security, support of modern technology and trust clearly outweigh the disadvantages like minimal overhead compared to pure HTTP. + +**Otherwise:** Attackers could perform man-in-the-middle attacks, spy on your users' behaviour and perform even more malicious actions when the connection is unencrypted + +🔗 [**Read More: Running a secure Node.js server**](/sections/security/secureserver.md) + +

+ +## ![✔] Comparing secret values and hashes securely + +**TL;DR:** When comparing secret values or hashes like HMAC digests, you should use the [`crypto.timingSafeEqual(a, b)`](https://nodejs.org/dist/latest-v9.x/docs/api/crypto.html#crypto_crypto_timingsafeequal_a_b) function Node provides out of the box since Node.js v6.6.0. This method compares two given objects and keeps comparing even if data does not match. The default equality comparison methods would simply return after a character mismatch, allowing timing attacks based on the operation length. + +**Otherwise:** Using default equality comparison operators you might expose critical information based on the time taken to compare two objects + +

+ +## ![✔] Generating random strings using Node.js + +**TL;DR:** Using a custom-built function generating pseudo-random strings for tokens and other security-sensitive use cases might actually not be as random as you think, rendering your application vulnerable to cryptographic attacks. When you have to generate secure random strings, use the [`crypto.RandomBytes(size, [callback])`](https://nodejs.org/dist/latest-v9.x/docs/api/crypto.html#crypto_crypto_randombytes_size_callback) function using available entropy provided by the system. + +**Otherwise:** When generating pseudo-random strings without cryptographically secure methods, attackers might predict and reproduce the generated results, rendering your application insecure + +

+ +Going on, below we've listed some important bits of advice from the OWASP project. + +## ![✔] OWASP A2: Broken Authentication + +- Require MFA/2FA for important services and accounts +- Rotate passwords and access keys frequently, including SSH keys +- Apply strong password policies, both for ops and in-application user management ([🔗 OWASP password recommendation](https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls.22)) +- Do not ship or deploy your application with any default credentials, particularly for admin users or external services you depend on +- Use only standard authentication methods like OAuth, OpenID, etc.  - **avoid** basic authentication +- Auth rate limiting: Disallow more than _X_ login attempts (including password recovery, etc.) in a period of _Y_ +- On login failure, don't let the user know whether the username or password verification failed, just return a common auth error +- Consider using a centralized user management system to avoid managing multiple accounts per employee (e.g. GitHub, AWS, Jenkins, etc) and to benefit from a battle-tested user management system + +## ![✔] OWASP A5:  Broken access control + +- Respect the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)  -  every component and DevOps person should only have access to the necessary information and resources +- **Never** work with the console/root (full-privilege) account except for account management +- Run all instances/containers on behalf of a role/service account +- Assign permissions to groups and not to users. This should make permission management easier and more transparent for most cases + +## ![✔] OWASP A6: Security Misconfiguration + +- Access to production environment internals is done through the internal network only, use SSH or other ways, but _never_ expose internal services +- Restrict internal network access  - explicitly set which resource can access other resources (e.g. network policy or subnets) +- If using cookies, configure it to "secured" mode where it's being sent over SSL only +- If using cookies, configure it for "same site" only so only requests from same domain will get back the designated cookies +- If using cookies, prefer "HttpOnly" configuration that prevent client-side JavaScript code from accessing the cookies +- Protect each VPC with strict and restrictive access rules +- Prioritize threats using any standard security threat modeling like STRIDE or DREAD +- Protect against DDoS attacks using HTTP(S) and TCP load balancers +- Perform periodic penetration tests by specialized agencies + +## ![✔] OWASP A3: Sensitive Data Exposure + +- Only accept SSL/TLS connections, enforce Strict-Transport-Security using headers +- Separate the network into segments (i.e. subnets) and ensure each node has the least necessary networking access permissions +- Group all services/instances that need no internet access and explicitly disallow any outgoing connection (a.k.a private subnet) +- Store all secrets in a vault products like AWS KMS, HashiCorp Vault or Google Cloud KMS +- Lockdown sensitive instance metadata using metadata +- Encrypt data in transit when it leaves a physical boundary +- Don't include secrets in log statements +- Avoid showing plain passwords in the frontend, take necessary measures in the backend and never store sensitive information in plaintext + +## ![✔] OWASP A9: Using Components With Known Security Vulneraibilities + +- Scan docker images for known vulnerabilities (using Docker's and other vendors offer scanning services) +- Enable automatic instance (machine) patching and upgrades to avoid running old OS versions that lack security patches +- Provide the user with both 'id', 'access' and 'refresh' token so the access token is short-lived and renewed with the refresh token +- Log and audit each API call to cloud and management services (e.g who deleted the S3 bucket?) using services like AWS CloudTrail +- Run the security checker of your cloud provider (e.g. AWS security trust advisor) + + +## ![✔] OWASP A10: Insufficient Logging & Monitoring + +- Alert on remarkable or suspicious auditing events like user login, new user creation, permission change, etc +- Alert on irregular amount of login failures (or equivelant actions like forgot password) +- Include the time and username that initiated the update in each DB record + +## ![✔] OWASP A7: Cross-Site-Scripting (XSS) + +- Use templating engines or frameworks that automatically escape XSS by design, such as EJS, Pug, React, or Angular. Learn the limitations of each mechanisms XSS protection and appropriately handle the use cases which are not covered +- Escaping untrusted HTTP request data based on the context in the HTML output (body, attribute, JavaScript, CSS, or URL) will resolve Reflected and Stored XSS vulnerabilities +- Applying context-sensitive encoding when modifying the browser document on the client-side acts against DOM XSS +- Enabling a Content-Security Policy (CSP) as a defense-in-depth mitigating control against XSS + + +


diff --git a/sections/security/dependencysecurity.french.md b/sections/security/dependencysecurity.french.md new file mode 100644 index 000000000..38abb514b --- /dev/null +++ b/sections/security/dependencysecurity.french.md @@ -0,0 +1,53 @@ +# Constantly and automatically inspect for vulnerable dependencies + +### One Paragraph Explainer + +The majority of Node.js applications rely heavily on a large number of third party modules from npm or Yarn, both popular package registries, due to ease and speed of development. However, the downside to this benefit is the security risks of including unknown vulnerabilities into your application, which is a risk recognised by its place in the OWASP top critical web application security risks list. + +There is a number of tools available to help identify third-party packages in Node.js applications which have been identified as vulnerable by the community to mitigate the risk of introducing them into your project. These can be used periodically from CLI tools or included as part of your application's build process. + +### Table of Contents + +- [NPM audit](#npm-audit) +- [Snyk](#snyk) +- [Greenkeeper](#greenkeeper) + +### NPM Audit + +`npm audit` is a new cli tool introduced with NPM@6. + +Running `npm audit` will produce a report of security vulnerabilities with the affected package name, vulnerability severity and description, path, and other information, and, if available, commands to apply patches to resolve vulnerabilities. + +![npm audit example](/assets/images/npm-audit.png) + +🔗 [Read on: NPM blog](https://docs.npmjs.com/getting-started/running-a-security-audit) + +### Snyk + +Snyk offers a feature-rich CLI, as well as GitHub integration. Snyk goes further with this and in addition to notifying vulnerabilities, also automatically creates new pull requests fixing vulnerabilities as patches are released for known vulnerabilities. + +Snyk's feature rich website also allows for ad-hoc assessment of dependencies when provided with a GitHub repository or npm module url. You can also search for npm packages which have vulnerabilities directly. + +An example of the output of the Synk GitHub integration automatically created pull request: +![synk GitHub example](/assets/images/snyk.png) + +🔗 [Read on: Snyk website](https://snyk.io/) + +🔗 [Read on: Synk online tool to check npm packages and GitHub modules](https://snyk.io/test) + +### Greenkeeper + +Greenkeeper is a service which offers real-time dependency updates, which keeps an application more secure by always using the most update to date and patched dependency versions. + +Greenkeeper watches the npm dependencies specified in a repository's `package.json` file, and automatically creates a working branch with each dependency update. The repository CI suite is then run to reveal any breaking changes for the updated dependency version in the application. If CI fails due to the dependency update, a clear and concise issue is created in the repository to be auctioned, outlining the current and updated package versions, along with information and commit history of the updated version. + +An example of the output of the Greenkeeper GitHub integration automatically created pull request: + +![synk github example](/assets/images/greenkeeper.png) +🔗 [Read on: Greenkeeper website](https://greenkeeper.io/) + +### Additional resources + +🔗 [Rising Stack Blog: Node.js dependency risks](https://blog.risingstack.com/controlling-node-js-security-risk-npm-dependencies/) + +🔗 [NodeSource Blog: Improving npm security](https://nodesource.com/blog/how-to-reduce-risk-and-improve-security-around-npm) diff --git a/sections/security/escape-output.french.md b/sections/security/escape-output.french.md new file mode 100644 index 000000000..baa65ec53 --- /dev/null +++ b/sections/security/escape-output.french.md @@ -0,0 +1,62 @@ +# Escape Output + +### One Paragraph Explainer + +HTML and other web languages mix content with executable code - a single HTML paragraph might contain a visual representation of data along with JavaScript execution instructions. When rendering HTML or returning data from API, what we believe is a pure content might actually embody JavaScript code that will get interpreted and executed by the browser. This happens, for example, when we render content that was inserted by an attacker to a database - for example `
`. This can be mitigated by instructing the browser to treat any chunk of untrusted data as content only and never interpret it - this technique is called escaping. Many npm libraries and HTML templating engines provide escaping capabilities (example: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi)). Not only HTML content should be escaped but also CSS and JavaScript + + +### Code example - Don't put untrusted data into your HTML + +```javascript + directly in a script + + inside an HTML comment + +
in an attribute name + + in a tag name + + directly in CSS + +``` + +### Code example - Malicious content that might be injected into a DB + +```javascript +
+ A pseudo comment to the a post + +
+ +``` + +

+ +### Blog Quote: "When we don’t want the characters to be interpreted" + +From the Blog [benramsey.com](https://benramsey.com/articles/escape-output/) +> Data may leave your application in the form of HTML sent to a Web browser, SQL sent to a database, XML sent to an RSS reader, WML sent to a wireless device, etc. The possibilities are limitless. Each of these has its own set of special characters that are interpreted differently than the rest of the plain text received. Sometimes we want to send these special characters so that they are interpreted (HTML tags sent to a Web browser, for example), while other times (in the case of input from users or some other source), we don’t want the characters to be interpreted, so we need to escape them. + +> Escaping is also sometimes referred to as encoding. In short, it is the process of representing data in a way that it will not be executed or interpreted. For example, HTML will render the following text in a Web browser as bold-faced text because the tags have special meaning: +This is bold text. +But, suppose I want to render the tags in the browser and avoid their interpretation. Then, I need to escape the angle brackets, which have special meaning in HTML. The following illustrates the escaped HTML: + +<strong>This is bold text.</strong> + + +

+ +### Blog Quote: "OWASP recommends using a security-focused encoding library" + +From the blog OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "Writing these encoders is not tremendously difficult, but there are quite a few hidden pitfalls. For example, you might be tempted to use some of the escaping shortcuts like \" in JavaScript. However, these values are dangerous and may be misinterpreted by the nested parsers in the browser. You might also forget to escape the escape character, which attackers can use to neutralize your attempts to be safe. **OWASP recommends using a security-focused encoding library to make sure these rules are properly implemented**." + + +

+ +### Blog Quote: "You MUST use the escape syntax for the part of the HTML" + +From the blog OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "But HTML entity encoding doesn't work if you're putting untrusted data inside a
`. Można to złagodzić, instruując przeglądarkę, aby traktowała każdą część niezaufanych danych tylko jako treść i nigdy jej nie interpretowała - ta technika nazywa się ucieczką. Wiele bibliotek NPM i silników szablonów HTML zapewnia możliwość zmiany znaczenia (przykład: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi)). Należy unikać ucieczki nie tylko treści HTML, ale także CSS i JavaScript + +### Przykład kodu - Nie umieszczaj niezaufanych danych w swoim HTML + +```javascript + directly in a script + + inside an HTML comment + +
in an attribute name + + in a tag name + + directly in CSS + +``` + +### Przykład kodu - złośliwe treści, które mogą zostać wstrzyknięte do bazy danych + +```javascript +
+ A pseudo comment to the a post + +
+ +``` + +

+ +### Cytat na blogu: „Gdy nie chcemy, aby znaki były interpretowane” + +Z Bloga [benramsey.com](https://benramsey.com/articles/escape-output/) +> Data may leave your application in the form of HTML sent to a Web browser, SQL sent to a database, XML sent to an RSS reader, WML sent to a wireless device, etc. The possibilities are limitless. Each of these has its own set of special characters that are interpreted differently than the rest of the plain text received. Sometimes we want to send these special characters so that they are interpreted (HTML tags sent to a Web browser, for example), while other times (in the case of input from users or some other source), we don’t want the characters to be interpreted, so we need to escape them. + +> Escaping is also sometimes referred to as encoding. In short, it is the process of representing data in a way that it will not be executed or interpreted. For example, HTML will render the following text in a Web browser as bold-faced text because the tags have special meaning: +This is bold text. +But, suppose I want to render the tags in the browser and avoid their interpretation. Then, I need to escape the angle brackets, which have special meaning in HTML. The following illustrates the escaped HTML: + +<strong>This is bold text.</strong> + + +

+ +### Cytat na blogu: "OWASP recommends using a security-focused encoding library" + +Z bloga OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "Writing these encoders is not tremendously difficult, but there are quite a few hidden pitfalls. For example, you might be tempted to use some of the escaping shortcuts like \" in JavaScript. However, these values are dangerous and may be misinterpreted by the nested parsers in the browser. You might also forget to escape the escape character, which attackers can use to neutralize your attempts to be safe. **OWASP recommends using a security-focused encoding library to make sure these rules are properly implemented**." + + +

+ +### Cytat na blogu: "You MUST use the escape syntax for the part of the HTML" + +Z bloga OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "But HTML entity encoding doesn't work if you're putting untrusted data inside a
`. Można to złagodzić, instruując przeglądarkę, aby traktowała każdą część niezaufanych danych tylko jako treść i nigdy jej nie interpretowała - ta technika nazywa się ucieczką. Wiele bibliotek NPM i silników szablonów HTML zapewnia możliwość zmiany znaczenia (przykład: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi)). Należy unikać ucieczki nie tylko treści HTML, ale także CSS i JavaScript From 210dcb257bbccaf4aa55d5c0c8f9ccbe0fd1c1b1 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:36:59 +0100 Subject: [PATCH 0200/1795] update expirejwt.polish.md fixed --- sections/security/expirejwt.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/expirejwt.polish.md b/sections/security/expirejwt.polish.md index d2d61fa1c..32335c7ec 100644 --- a/sections/security/expirejwt.polish.md +++ b/sections/security/expirejwt.polish.md @@ -1,6 +1,6 @@ # Obsługa czarnych list JWT -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Z założenia JWT (JSON Web Tokens) są całkowicie bezstanowe, więc po podpisaniu ważnego tokena przez wystawcę token może zostać zweryfikowany przez aplikację jako autentyczny. Problemem z tym związanym jest problem związany z bezpieczeństwem, w którym przeciekający token może być nadal używany i nie może zostać odwołany, ponieważ podpis pozostaje ważny, dopóki podpis dostarczony przez problemy odpowiada oczekiwaniom aplikacji. Z tego powodu podczas korzystania z uwierzytelniania JWT aplikacja powinna zarządzać czarną listą wygasłych lub odwołanych tokenów, aby zachować bezpieczeństwo użytkownika na wypadek, gdyby token musiał zostać odwołany. From 28592add1677948ad3a28fe88daeb718e9f790cc Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:37:24 +0100 Subject: [PATCH 0201/1795] update hideerrors.polish.md fixed --- sections/security/hideerrors.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/hideerrors.polish.md b/sections/security/hideerrors.polish.md index 29212484b..8997e0d77 100644 --- a/sections/security/hideerrors.polish.md +++ b/sections/security/hideerrors.polish.md @@ -1,6 +1,6 @@ # Ukryj szczegóły błędu przed klientem -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Należy unikać ujawniania klientowi szczegółów błędu aplikacji podczas produkcji ze względu na ryzyko ujawnienia wrażliwych szczegółów aplikacji, takich jak ścieżki plików serwera, używane moduły innych firm i inne wewnętrzne przepływy pracy aplikacji, które mogą zostać wykorzystane przez atakującego. Express jest wyposażony we wbudowany moduł obsługi błędów, który zajmuje się wszelkimi błędami, które mogą wystąpić w aplikacji. Ta domyślna funkcja oprogramowania pośredniego obsługująca błędy jest dodawana na końcu stosu funkcji oprogramowania pośredniego. From d5c557b3816602b3dd8c2608c6f73ada7c021b0b Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:37:50 +0100 Subject: [PATCH 0202/1795] update limitrequests.polish.md fixed --- sections/security/limitrequests.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/limitrequests.polish.md b/sections/security/limitrequests.polish.md index d9aae7150..b93537611 100644 --- a/sections/security/limitrequests.polish.md +++ b/sections/security/limitrequests.polish.md @@ -1,6 +1,6 @@ # Ogranicz równoczesne żądania za pomocą modułu równoważącego lub oprogramowania pośredniego -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem W aplikacji należy wprowadzić ograniczenie szybkości, aby chronić aplikację Node.js przed przytłoczeniem zbyt dużą liczbą żądań jednocześnie. Ograniczanie prędkości jest zadaniem najlepiej wykonywanym za pomocą usługi zaprojektowanej do tego zadania, takiej jak nginx, jednak jest to również możliwe przy użyciu opcji [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) pakietu lub oprogramowania pośredniego, takiego jak [express-rate-limiter](https://www.npmjs.com/package/express-rate-limit) dla aplikacji Express.js. From 0bf5fbe6c7ab1e89440d3b02c8ef86ad8a38c22f Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:38:14 +0100 Subject: [PATCH 0203/1795] update lintrules.polish.md fixed --- sections/security/lintrules.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/lintrules.polish.md b/sections/security/lintrules.polish.md index 657b93708..e1c391416 100644 --- a/sections/security/lintrules.polish.md +++ b/sections/security/lintrules.polish.md @@ -1,6 +1,6 @@ # Ustanowienie zasad bezpieczeństwa linter -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Wtyczki bezpieczeństwa dla ESLint i TSLint, takie jak [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) i [tslint-config-security](https://www.npmjs.com/package/tslint-config-security) oferowują kontrole bezpieczeństwa kodu na podstawie szeregu znanych luk, takich jak niebezpieczne RegEx, niebezpieczne użycie `eval ()` i nieliterowe nazwy plików używane podczas uzyskiwania dostępu do systemu plików w aplikacji. Korzystanie z git hooks, takich jak [pre-git](https://github.com/bahmutov/pre-git), pozwala na dalsze egzekwowanie reguł dotyczących kontroli źródła, zanim zostaną one przekazane zdalnym, z których jednym może być sprawdzenie że do kontroli źródła nie dodano żadnych danych wrażliwych. From 4f7263663ad957550a279af14ae094fa38dd83b9 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:38:40 +0100 Subject: [PATCH 0204/1795] update login-rate-limit.polish.md fixed --- sections/security/login-rate-limit.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/login-rate-limit.polish.md b/sections/security/login-rate-limit.polish.md index f8d0fa745..70fe596f1 100644 --- a/sections/security/login-rate-limit.polish.md +++ b/sections/security/login-rate-limit.polish.md @@ -1,6 +1,6 @@ # Zapobieganie atakom brute-force na autoryzację -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Pozostawienie wyżej uprzywilejowanych tras, takich jak `/ login` lub` / admin`, ujawnione bez ograniczenia wyjść, naraża aplikację na ataki słownikowe z użyciem siły brute force. Użycie strategii w celu ograniczenia żądań do takich tras może zapobiec powodzeniu, ograniczając liczbę prób zezwolenia na podstawie właściwości żądania, takiej jak ip, lub parametru treści, takiego jak nazwa użytkownika / adres e-mail. From ad4cad9375fa88237ca175b00c08a4b1babed9e3 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:39:08 +0100 Subject: [PATCH 0205/1795] update non-root-user.polish.md fixed --- sections/security/non-root-user.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/non-root-user.polish.md b/sections/security/non-root-user.polish.md index 96a111a07..6ad823c6c 100644 --- a/sections/security/non-root-user.polish.md +++ b/sections/security/non-root-user.polish.md @@ -1,6 +1,6 @@ # Uruchom Node.js jako Non-Root User -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Zgodnie z „zasadą najmniejszych uprawnień” użytkownik / proces musi mieć dostęp tylko do niezbędnych informacji i zasobów. Przydzielenie rootowi dostępu, atakującemu otwiera zupełnie nowy świat złośliwych pomysłów, takich jak kierowanie ruchu do innych serwerów. W praktyce większość aplikacji Node.js nie potrzebuje dostępu do konta root i nie działa z takimi uprawnieniami. Istnieją jednak dwa typowe scenariusze, które mogą wypychać do używania roota: From abc6546d72245c1476f77321bace259b4074668b Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:39:33 +0100 Subject: [PATCH 0206/1795] update ormodmusage.polish.md fixed --- sections/security/ormodmusage.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/ormodmusage.polish.md b/sections/security/ormodmusage.polish.md index bea4991a1..04d39164e 100644 --- a/sections/security/ormodmusage.polish.md +++ b/sections/security/ormodmusage.polish.md @@ -1,6 +1,6 @@ # Zapobieganie podatności na wstrzykiwanie bazy danych za pomocą bibliotek ORM / ODM lub innych pakietów DAL -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Podczas tworzenia logiki bazy danych należy uważać na ewentualne wektory wstrzykiwania, które mogą zostać wykorzystane przez potencjalnych atakujących. Ręczne pisanie zapytań do bazy danych, bez sprawdzania poprawności danych dla żądań użytkowników, jest najłatwiejszym sposobem na uwzględnienie tych luk. Sytuacji tej można jednak łatwo uniknąć, używając odpowiednich pakietów do sprawdzania poprawności danych wejściowych i obsługi operacji na bazie danych. W wielu przypadkach Twój system będzie bezpieczny i będzie działał przy użyciu biblioteki sprawdzania poprawności, takiej jak [joi](https://github.com/hapijs/joi) lub [yup](https://github.com/jquense/yup) i ORM / ODM z poniższej listy. Powinno to zagwarantować użycie sparametryzowanych zapytań i powiązań danych, aby zapewnić prawidłowe ucieczkę zweryfikowanych danych i obsługę ich bez otwierania niepożądanych wektorów ataku. Wiele z tych bibliotek ułatwi Ci życie jako programista, umożliwiając wiele przydatnych funkcji, takich jak brak konieczności ręcznego pisania skomplikowanych zapytań, dostarczanie typów dla systemów typów opartych na języku lub konwertowanie typów danych na pożądane formaty. Podsumowując: __zawsze__ sprawdzaj poprawność danych, które zamierzasz przechowywać, i używaj odpowiednich bibliotek mapowania danych do obsługi niebezpiecznej pracy. From 6cd60f853bee12b01ee4adaa3e49a683e5f43692 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:40:00 +0100 Subject: [PATCH 0207/1795] update regex.polish.md fixed --- sections/security/regex.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/regex.polish.md b/sections/security/regex.polish.md index 6fc6c90de..bb6d91a4a 100644 --- a/sections/security/regex.polish.md +++ b/sections/security/regex.polish.md @@ -1,6 +1,6 @@ # Zapobiegaj złośliwemu RegExowi przeciążania wykonania pojedynczego wątku -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Ryzykiem nieodłącznie związanym z używaniem wyrażeń regularnych są zasoby obliczeniowe, które wymagają analizy tekstu i dopasowania do określonego wzorca. W przypadku platformy Node.js, w której dominuje jednowątkowa pętla zdarzeń, operacja związana z procesorem, taka jak rozwiązywanie wzorca wyrażeń regularnych, spowoduje, że aplikacja nie będzie odpowiadać. Jeśli to możliwe, unikaj RegEx lub odrocz zadanie do dedykowanej biblioteki, takiej jak [validator.js](https://github.com/chriso/validator.js) lub [safe-regex](https://github.com/substack/safe-regex), aby sprawdzić, czy wzorzec RegEx jest bezpieczny. From e7ec9924b5fc45672a92a8034d92ca51482ebfe3 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:40:49 +0100 Subject: [PATCH 0208/1795] update requestpayloadsizelimit.polish.md fixed --- sections/security/requestpayloadsizelimit.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/requestpayloadsizelimit.polish.md b/sections/security/requestpayloadsizelimit.polish.md index e8f3d5ab5..ea665650e 100644 --- a/sections/security/requestpayloadsizelimit.polish.md +++ b/sections/security/requestpayloadsizelimit.polish.md @@ -1,6 +1,6 @@ # Ogranicz rozmiar payloada przy użyciu odwrotnego proxy lub oprogramowania pośredniego -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Analizowanie treści żądań, na przykład payloadów zakodowanych w JSON, jest operacją wymagającą dużej wydajności, szczególnie w przypadku większych żądań. Podczas obsługi żądań przychodzących w aplikacji internetowej należy ograniczyć rozmiar odpowiednich payloadów. Przychodzące requesty z From 87a425235b1f7e1ccda71db442c72e9210670e98 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:41:18 +0100 Subject: [PATCH 0209/1795] update safemoduleloading.polish.md fixed --- sections/security/safemoduleloading.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/safemoduleloading.polish.md b/sections/security/safemoduleloading.polish.md index 5aa37d52f..72f62acab 100644 --- a/sections/security/safemoduleloading.polish.md +++ b/sections/security/safemoduleloading.polish.md @@ -1,6 +1,6 @@ # Unikaj ładowania modułu za pomocą zmiennej -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Unikaj wymagania / importowania innego pliku ze ścieżką podaną jako parametr ze względu na obawy, że mógł on pochodzić z danych wejściowych użytkownika. Regułę tę można rozszerzyć w celu uzyskania ogólnego dostępu do plików (tj. `Fs.readFile()`) lub innych wrażliwych zasobów ze zmiennymi dynamicznymi pochodzącymi z danych wprowadzanych przez użytkownika. From 6a1c25e0b26dbb6a55198a738d06c22a60f0bea5 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:41:43 +0100 Subject: [PATCH 0210/1795] update saferedirects.polish.md fixed --- sections/security/saferedirects.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/saferedirects.polish.md b/sections/security/saferedirects.polish.md index 16a65bf07..486a9abff 100644 --- a/sections/security/saferedirects.polish.md +++ b/sections/security/saferedirects.polish.md @@ -1,6 +1,6 @@ # Zapobiegaj niebezpiecznym przekierowaniom -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Gdy przekierowania są implementowane w Node.js i/lub Express, ważne jest, aby przeprowadzić weryfikację danych wejściowych po stronie serwera. Jeśli osoba atakująca odkryje, że nie weryfikujesz danych zewnętrznych dostarczonych przez użytkownika, może wykorzystać tę lukę, publikując specjalnie spreparowane łącza na forach, w mediach społecznościowych i innych miejscach publicznych, aby użytkownicy mogli ją kliknąć. From 3ee4ceae60861846211335d3de69ce8ab70f1d9a Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:42:10 +0100 Subject: [PATCH 0211/1795] update sandbox.polish.md fixed --- sections/security/sandbox.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/sandbox.polish.md b/sections/security/sandbox.polish.md index bbd422e82..5c21f2b3d 100644 --- a/sections/security/sandbox.polish.md +++ b/sections/security/sandbox.polish.md @@ -1,6 +1,6 @@ #Uruchom niebezpieczny kod w piaskownicy -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Z zasady należy uruchamiać tylko własne pliki JavaScript. Pomijając teorie, scenariusze w świecie rzeczywistym wymagają wykonywania plików JavaScript przekazywanych dynamicznie w czasie wykonywania. Rozważmy na przykład strukturę dynamiczną, taką jak webpack, która akceptuje niestandardowe programy ładujące i wykonuje je dynamicznie podczas kompilacji. W obecności szkodliwych wtyczek chcemy zminimalizować szkody, a może nawet pozwolić na pomyślne zakończenie przepływu - wymaga to uruchomienia wtyczek w środowisku piaskownicy, które jest w pełni izolowane pod względem zasobów, awarii i informacji, którymi się z nim dzielimy. Trzy główne opcje mogą pomóc w osiągnięciu tej izolacji: From 790fecde8a67d8f814147cebb0334e8a47ad0821 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:42:34 +0100 Subject: [PATCH 0212/1795] update secretmanagement.polish.md fixed --- sections/security/secretmanagement.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/secretmanagement.polish.md b/sections/security/secretmanagement.polish.md index cea1167c8..b70b3ebb2 100644 --- a/sections/security/secretmanagement.polish.md +++ b/sections/security/secretmanagement.polish.md @@ -1,6 +1,6 @@ # Wyodrębnij dane wrażliwe z plików konfiguracyjnych lub użyj pakietu npm, który je szyfruje -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Najczęstszym i bezpiecznym sposobem zapewnienia dostępu aplikacji Node.js do kluczy i kluczy tajnych jest przechowywanie ich przy użyciu zmiennych środowiskowych w systemie, w którym jest uruchomiona. Po ustawieniu można uzyskać do nich dostęp z globalnego obiektu `process.env`. Testem lakmusowym sprawdzającym, czy aplikacja poprawnie skonfigurowała konfigurację z kodu, jest to, czy baza kodu może zostać w dowolnym momencie otwarta, bez narażania jakichkolwiek poświadczeń. From 1272e5da3c74854986ca247493c2e968839bba03 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:42:57 +0100 Subject: [PATCH 0213/1795] update secureheaders.polish.md fixed --- sections/security/secureheaders.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/secureheaders.polish.md b/sections/security/secureheaders.polish.md index 10616880f..a1b7a8683 100644 --- a/sections/security/secureheaders.polish.md +++ b/sections/security/secureheaders.polish.md @@ -3,7 +3,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Istnieją nagłówki związane z bezpieczeństwem, które służą do dalszego zabezpieczenia aplikacji. Najważniejsze nagłówki są wymienione poniżej. Możesz również odwiedzić witryny połączone na dole tej strony, aby uzyskać więcej informacji na ten temat. Możesz łatwo ustawić te nagłówki za pomocą modułu [Helmet](https://www.npmjs.com/package/helmet) modułu dla express ([Helmet for koa](https://www.npmjs.com/package/koa-helmet)). From ce13ce50caa9298cbb2f2b88ccccd07e675932af Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:43:24 +0100 Subject: [PATCH 0214/1795] update sessions.polish.md fixed --- sections/security/sessions.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/sessions.polish.md b/sections/security/sessions.polish.md index 97804108d..c091e4373 100644 --- a/sections/security/sessions.polish.md +++ b/sections/security/sessions.polish.md @@ -3,7 +3,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Wiele popularnych programów pośrednich sesji nie stosuje ustawień najlepszych praktyk / bezpiecznych plików cookie po wyjęciu z box'a. Dostosowanie tych ustawień w stosunku do ustawień domyślnych zapewnia lepszą ochronę zarówno dla użytkownika, jak i aplikacji, poprzez zmniejszenie zagrożenia atakami, takimi jak przejęcie sesji i identyfikacja sesji. From f58e57b19530fad741e1590d44b8d2824656c7dd Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:43:47 +0100 Subject: [PATCH 0215/1795] update validation.polish.md fixed --- sections/security/validation.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/validation.polish.md b/sections/security/validation.polish.md index 4874bc527..f1c39eb34 100644 --- a/sections/security/validation.polish.md +++ b/sections/security/validation.polish.md @@ -1,6 +1,6 @@ # Sprawdź poprawność przychodzących schematów JSON -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Sprawdzanie poprawności polega na tym, aby bardzo jasno określić, jaki payload nasza aplikacja jest gotowa zaakceptować i szybko zawieść, jeśli dane wejściowe odbiegają od oczekiwań. Minimalizuje to powierzchnię atakujących, którzy nie mogą już wypróbowywać payloads o innej strukturze, wartościach i długości. Praktycznie zapobiega atakom takim jak DDOS (kod prawdopodobnie nie zawiedzie, gdy dane wejściowe są dobrze zdefiniowane) i Insecure Deserialization (JSON nie zawiera niespodzianek). Chociaż sprawdzanie poprawności można kodować lub polegać na klasach i typach (klasy TypeScript, ES6), społeczność wydaje się coraz bardziej lubić schematy oparte na JSON, ponieważ pozwalają one na deklarowanie złożonych reguł bez kodowania i dzielą się oczekiwaniami z interfejsem użytkownika. Schemat JSON to nowy standard, który jest obsługiwany przez wiele bibliotek i narzędzi npm (np. [Jsonschema](https://www.npmjs.com/package/jsonschema), [Postman](http://blog.getpostman.com/2017/07/28/api-testing-tips-from-a-postman-professional/)), [joi](https://www.npmjs.com/package/joi) jest również bardzo popularny ze słodką składnią. Zazwyczaj składnia JSON nie może obejmować wszystkich scenariuszy sprawdzania poprawności i przydają się niestandardowe kody lub wstępnie przygotowane ramy sprawdzania poprawności, takie jak [validator.js](https://github.com/chriso/validator.js/). Bez względu na wybraną składnię upewnij się, aby uruchomić sprawdzanie poprawności tak wcześnie, jak to możliwe - na przykład, używając oprogramowania pośredniego Express, które sprawdza poprawność treści żądania przed przekazaniem żądania do procedury obsługi trasy From b41c38b04d7f14a087985292821a793076e784b7 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:45:17 +0100 Subject: [PATCH 0216/1795] update asyncerrorhandling.polish.md fixed --- sections/errorhandling/asyncerrorhandling.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/asyncerrorhandling.polish.md b/sections/errorhandling/asyncerrorhandling.polish.md index 2e5c7fe2e..3f51d13ef 100644 --- a/sections/errorhandling/asyncerrorhandling.polish.md +++ b/sections/errorhandling/asyncerrorhandling.polish.md @@ -1,6 +1,6 @@ # Użyj Async-Await lub promises do obsługi błędów asynchronicznych -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Callbacks nie skalują się dobrze, ponieważ większość programistów nie zna ich. Zmuszają do sprawdzania błędów, radzenia sobie z nieprzyjemnym zagnieżdżaniem kodu i utrudniają rozumowanie przepływu kodu. Biblioteki promises takie jak BlueBird, async i Q pakują standardowy styl kodu za pomocą RETURN i THROW do sterowania przebiegiem programu. W szczególności obsługują ulubiony styl obsługi błędów try-catch, który pozwala uwolnić główną ścieżkę kodu od radzenia sobie z błędami w każdej funkcji From da7a368c4d3acfdc9cd98fcddd0ee253ec7f23cd Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:45:43 +0100 Subject: [PATCH 0217/1795] update catchunhandledpromiserejection.polish.md fixed --- sections/errorhandling/catchunhandledpromiserejection.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/catchunhandledpromiserejection.polish.md b/sections/errorhandling/catchunhandledpromiserejection.polish.md index d51c10016..4d5330016 100644 --- a/sections/errorhandling/catchunhandledpromiserejection.polish.md +++ b/sections/errorhandling/catchunhandledpromiserejection.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Zazwyczaj większość współczesnego kodu aplikacji Node.js / Express działa w ramach obietnic - czy to w .then handler, wywołaniu zwrotnym funkcji, czy w bloku catch. Zaskakujące jest to, że jeśli programista nie pamięta o dodaniu klauzuli .catch, błędy zgłaszane w tych miejscach nie są obsługiwane przez moduł obsługi zdarzeń uncaughtException i znikają. Najnowsze wersje Node dodały komunikat ostrzegawczy, gdy pojawia się nieobsługiwane odrzucenie, chociaż może to pomóc w zauważeniu, że coś pójdzie nie tak, ale oczywiście nie jest to właściwa metoda obsługi błędów. Prostym rozwiązaniem jest, aby nigdy nie zapomnieć o dodaniu klauzul .catch w każdym wywołaniu łańcucha obietnicy i przekierowaniu do scentralizowanej procedury obsługi błędów. Jednak budowanie strategii radzenia sobie z błędami wyłącznie w oparciu o dyscyplinę programisty jest dość kruche. W związku z tym zaleca się stosowanie płynnego wycofywania się i zapisanie się na `process.on ('unhandledRejection', callback)` - zapewni to, że każdy błąd obietnicy, jeśli nie zostanie złapany lokalnie, zostanie obsłużony. From 04edb5c1c680ee28d2f3712fd9fe82e04263f80d Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:46:21 +0100 Subject: [PATCH 0218/1795] update failfast.polish.md fixed --- sections/errorhandling/failfast.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/failfast.polish.md b/sections/errorhandling/failfast.polish.md index 681db9a84..57ff9de22 100644 --- a/sections/errorhandling/failfast.polish.md +++ b/sections/errorhandling/failfast.polish.md @@ -1,6 +1,6 @@ # Szybko się nie powiedzie, sprawdź poprawność argumentów za pomocą dedykowanej biblioteki -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Wszyscy wiemy, jak sprawdzanie argumentów i szybkie niepowodzenie jest ważne, aby uniknąć ukrytych błędów (patrz przykład kodu anty-wzorca poniżej). Jeśli nie, przeczytaj o programowaniu jawnym i programowaniu defensywnym. W rzeczywistości staramy się go unikać ze względu na irytację związaną z jego kodowaniem (np. myśl o sprawdzeniu poprawności hierarchicznego obiektu JSON za pomocą pól takich jak e-mail i daty) - biblioteki takie jak Joi i Validator zmieniają to żmudne zadanie w bryłę. From d046c3384948358528838f51f5296a0a74b6354e Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:46:52 +0100 Subject: [PATCH 0219/1795] update testingerrorflows.polish.md fixed --- sections/errorhandling/testingerrorflows.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/testingerrorflows.polish.md b/sections/errorhandling/testingerrorflows.polish.md index 5b0e70de8..7cf2f347b 100644 --- a/sections/errorhandling/testingerrorflows.polish.md +++ b/sections/errorhandling/testingerrorflows.polish.md @@ -1,6 +1,6 @@ # Przepływy błędów testowych przy użyciu ulubionego środowiska testowego -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Testowanie „szczęśliwych” ścieżek nie jest lepsze niż testowanie błędów. Dobry zasięg kodu testowego wymaga testowania wyjątkowych ścieżek. W przeciwnym razie nie ma zaufania, że wyjątki rzeczywiście są obsługiwane poprawnie. Każda platforma testowania jednostek, jak [Mocha](https://mochajs.org/) i [Chai](http://chaijs.com/), obsługuje testowanie wyjątków (przykłady kodu poniżej). Jeśli okaże się, że testowanie każdej funkcji wewnętrznej i wyjątku jest uciążliwe, możesz zadowolić się testowaniem tylko błędów HTTP interfejsu REST API. From c1e7897479209dadb9cbd9ea2e77367bd72502bf Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:47:17 +0100 Subject: [PATCH 0220/1795] update usematurelogger.polish.md fixed --- sections/errorhandling/usematurelogger.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/usematurelogger.polish.md b/sections/errorhandling/usematurelogger.polish.md index e71e711da..d8d407c52 100644 --- a/sections/errorhandling/usematurelogger.polish.md +++ b/sections/errorhandling/usematurelogger.polish.md @@ -1,6 +1,6 @@ # Użyj dojrzałego programu rejestrującego, aby zwiększyć widoczność błędów -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Wszyscy kochamy console.log, ale oczywiście poważny projekt to renomowany i trwały rejestrator, taki jak [Winston] [winston] (bardzo popularny) lub [Pino] [pino] (nowy dzieciak w mieście, który koncentruje się na wydajności). Zestaw praktyk i narzędzi pomoże znacznie szybciej uzasadnić błędy - (1) często rejestruj dane przy użyciu różnych poziomów (debugowanie, informacje, błąd), (2) podczas logowania, podaj informacje kontekstowe jako obiekty JSON, patrz przykład poniżej. (3) Oglądaj i filtruj dzienniki za pomocą interfejsu API do wysyłania zapytań (wbudowanego w większość programów rejestrujących) lub oprogramowania do przeglądania dzienników. (4) Ujawnij i wyślij oświadczenie dziennika dla zespołu operacyjnego za pomocą narzędzi wywiadu operacyjnego, takich jak Splunk. From a9f7bff99f6348918095a393bfaed5d28e70c60c Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:47:43 +0100 Subject: [PATCH 0221/1795] update useonlythebuiltinerror.polish.md fixed --- sections/errorhandling/useonlythebuiltinerror.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/useonlythebuiltinerror.polish.md b/sections/errorhandling/useonlythebuiltinerror.polish.md index 4eda87a58..f45feeed3 100644 --- a/sections/errorhandling/useonlythebuiltinerror.polish.md +++ b/sections/errorhandling/useonlythebuiltinerror.polish.md @@ -1,6 +1,6 @@ # Używaj tylko wbudowanego obiektu Error -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Zezwalająca natura JavaScript wraz z różnorodnymi opcjami przepływu kodu (np. EventEmitter, Callbacki, Promises itp.) popycha do dużej rozbieżności w sposobie, w jaki programiści zgłaszają błędy - niektóre ciągi znaków, inne definiują własne typy niestandardowe. Korzystanie z wbudowanego obiektu Error Node.js pomaga zachować jednolitość w kodzie, a dzięki bibliotekom stron trzecich zachowuje również istotne informacje, takie jak StackTrace. Zgłaszając wyjątek, zwykle dobrą praktyką jest wypełnienie go dodatkowymi właściwościami kontekstowymi, takimi jak nazwa błędu i powiązany kod błędu HTTP. Aby osiągnąć tę jednolitość i praktyki, rozważ rozszerzenie obiektu Error o dodatkowe właściwości, ale uważaj, aby go nie przesadzić. Ogólnie dobrym pomysłem jest rozszerzenie wbudowanego obiektu Error tylko raz o AppError dla wszystkich błędów na poziomie aplikacji i przekazanie wszelkich danych potrzebnych do rozróżnienia różnych rodzajów błędów jako argumentów. Nie trzeba wielokrotnie rozszerzać obiektu Error (jeden dla każdego przypadku błędu, takiego jak DbError, HttpError) Zobacz przykłady kodu poniżej From c410a988af05f98b58db329237539e55102ad9b7 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:48:16 +0100 Subject: [PATCH 0222/1795] update nativeoverutil.polish.md fixed --- sections/performance/nativeoverutil.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/performance/nativeoverutil.polish.md b/sections/performance/nativeoverutil.polish.md index 60acee3dc..9df041bda 100644 --- a/sections/performance/nativeoverutil.polish.md +++ b/sections/performance/nativeoverutil.polish.md @@ -3,7 +3,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Czasami użycie metod natywnych jest lepsze niż wymaganie _lodash_ lub _underscore_, ponieważ te biblioteki mogą prowadzić do utraty wydajności lub zajmować więcej miejsca niż potrzeba. Wydajność przy użyciu metod rodzimych skutkuje [ogólnym ~50% zyskiem](https://github.com/Berkmann18/NativeVsUtils/blob/master/analysis.xlsx), który obejmuje następujące metody: `Array.concat`,` Array.fill`, `Array.filter`, `Array.map`, `(Array|String).indexOf`, `Object.find`, ... From 235f235413e7633ee39707f31ffd0b99c11e458b Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:49:10 +0100 Subject: [PATCH 0223/1795] update LTSrelease.polish.md fixed --- sections/production/LTSrelease.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/LTSrelease.polish.md b/sections/production/LTSrelease.polish.md index 6f04eff4c..6382b5e5a 100644 --- a/sections/production/LTSrelease.polish.md +++ b/sections/production/LTSrelease.polish.md @@ -1,6 +1,6 @@ # Użyj wersji LTS Node.js w produkcji -### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Upewnij się, że korzystasz z wersji LTS (Long Term Support) Node.js w produkcji, aby otrzymywać krytyczne poprawki błędów, aktualizacje zabezpieczeń i ulepszenia wydajności. From c3ba397999057b2eb5f049dc03828a64c9a87ec3 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:49:40 +0100 Subject: [PATCH 0224/1795] update assigntransactionid.polish.md fixed --- sections/production/assigntransactionid.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/assigntransactionid.polish.md b/sections/production/assigntransactionid.polish.md index b9d12c531..866d3833a 100644 --- a/sections/production/assigntransactionid.polish.md +++ b/sections/production/assigntransactionid.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Typowy dziennik to magazyn wpisów ze wszystkich komponentów i żądań. Po wykryciu jakiejś podejrzanej linii lub błędu staje się pokręcony, aby dopasować inne linie należące do tego samego określonego przepływu (np. użytkownik „John” próbował coś kupić). Staje się to jeszcze bardziej krytyczne i trudne w środowisku mikrousług, gdy żądanie / transakcja może obejmować wiele komputerów. Należy rozwiązać ten problem, przypisując unikalną wartość identyfikatora transakcji do wszystkich wpisów z tego samego żądania, aby po wykryciu jednej linii można skopiować identyfikator i wyszukać każdą linię o podobnym identyfikatorze transakcji. Jednak osiągnięcie tego w Node nie jest proste, ponieważ pojedynczy wątek służy do obsługi wszystkich żądań - rozważ użycie biblioteki, która może grupować dane na poziomie żądań - patrz przykład kodu na następnym slajdzie. Podczas wywoływania innej mikrousługi przekaż identyfikator transakcji za pomocą nagłówka HTTP, takiego jak „x-transaction-id”, aby zachować ten sam kontekst. From 1d24f77e72c389fd096a8dd1fe68242ad768d9aa Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:50:06 +0100 Subject: [PATCH 0225/1795] update createmaintenanceendpoint.polish.md fixed --- sections/production/createmaintenanceendpoint.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/createmaintenanceendpoint.polish.md b/sections/production/createmaintenanceendpoint.polish.md index 2a7fc4ea7..5ba559493 100644 --- a/sections/production/createmaintenanceendpoint.polish.md +++ b/sections/production/createmaintenanceendpoint.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Punkt końcowy konserwacji to wysoce bezpieczny interfejs API HTTP, który jest częścią kodu aplikacji, a jego celem jest wykorzystanie go przez zespół operacyjny / produkcyjny do monitorowania i udostępniania funkcji konserwacji. Na przykład może zwrócić zrzut stosu (migawkę pamięci) procesu, zgłosić, czy występują wycieki pamięci, a nawet pozwolić na bezpośrednie wykonywanie poleceń REPL. Ten punkt końcowy jest potrzebny tam, gdzie konwencjonalne narzędzia DevOps (produkty do monitorowania, logi itp.) Nie zbierają niektórych określonych informacji lub nie kupujesz / nie instalujesz takich narzędzi. Złotą zasadą jest stosowanie profesjonalnych i zewnętrznych narzędzi do monitorowania i utrzymania produkcji, które są zwykle bardziej niezawodne i dokładne. To powiedziawszy, mogą wystąpić przypadki, w których ogólne narzędzia nie wyodrębnią informacji specyficznych dla Node lub aplikacji - na przykład, jeśli chcesz wygenerować migawkę pamięci w momencie, gdy GC zakończy cykl - kilka bibliotek npm chętnie to zrobi za Ciebie, ale popularne narzędzia do monitorowania prawdopodobnie nie będą miały tej funkcji. Ważne jest, aby zachować ten punkt końcowy jako prywatny i dostępny tylko dla administratorów, ponieważ może on stać się celem ataku DDOS. From 57ae7fee55f9ceaf1c5322389a0ac1c278b22a5a Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:50:32 +0100 Subject: [PATCH 0226/1795] update delegatetoproxy.polish.md fixed --- sections/production/delegatetoproxy.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/delegatetoproxy.polish.md b/sections/production/delegatetoproxy.polish.md index 886bd702f..0f8997959 100644 --- a/sections/production/delegatetoproxy.polish.md +++ b/sections/production/delegatetoproxy.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem To bardzo kuszące dla kultowego Expressa i korzystania z bogatej oferty oprogramowania pośredniego do zadań związanych z siecią, takich jak serwowanie plików statycznych, kodowanie gzip, żądania ograniczania przepustowości, zakończenie SSL itp. Jest to zabójstwo wydajności ze względu na model jednowątkowy, który zachowa procesor zajęty przez długi czas (pamiętaj, że model wykonania węzła jest zoptymalizowany do krótkich zadań lub asynchronicznych zadań związanych z We / Wy). Lepszym rozwiązaniem jest użycie narzędzia, które specjalizuje się w zadaniach sieciowych - najbardziej popularne to nginx i HAproxy, które są również używane przez największych dostawców usług w chmurze, aby zmniejszyć obciążenie procesów procesowych Node.js. From 197655ad883b1435a3d92b119da1545bbe7ed399 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:50:57 +0100 Subject: [PATCH 0227/1795] update detectvulnerabilities.polish.md fixed --- sections/production/detectvulnerabilities.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/detectvulnerabilities.polish.md b/sections/production/detectvulnerabilities.polish.md index e9be37181..d089f91c3 100644 --- a/sections/production/detectvulnerabilities.polish.md +++ b/sections/production/detectvulnerabilities.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Nowoczesne aplikacje Node mają dziesiątki, a czasem setki zależności. Jeśli którakolwiek z zależności, z której korzystasz ma znaną lukę w zabezpieczeniach, Twoja aplikacja też. From e703930823caea09422d97a006419824fc578e0f Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:51:34 +0100 Subject: [PATCH 0228/1795] update logrouting.polish.md fixed --- sections/production/logrouting.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/logrouting.polish.md b/sections/production/logrouting.polish.md index 956356bbc..0b2a278e3 100644 --- a/sections/production/logrouting.polish.md +++ b/sections/production/logrouting.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Kod aplikacji nie powinien obsługiwać routingu dziennika, ale zamiast tego powinien używać narzędzia logger do pisania w `stdout / stderr`. „Log routing” oznacza pobieranie i przekazywanie dzienników do innej lokalizacji niż aplikacja lub proces aplikacji, na przykład zapisywanie dzienników w pliku, bazie danych itp. Powód tego jest w większości dwojaki: 1) separation of concerns oraz 2) [12-Factor best practices for modern applications](https://12factor.net/logs). From a8e36dc3f676dd6deca328ad4f250619feb9d21e Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:51:57 +0100 Subject: [PATCH 0229/1795] update measurememory.polish.md fixed --- sections/production/measurememory.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/measurememory.polish.md b/sections/production/measurememory.polish.md index ba5ef3e2a..ef31ca86d 100644 --- a/sections/production/measurememory.polish.md +++ b/sections/production/measurememory.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem W idealnym świecie programista nie powinien zajmować się wyciekami pamięci. W rzeczywistości problemy z pamięcią to znane problemy węzłów, o których trzeba pamiętać. Przede wszystkim należy stale monitorować zużycie pamięci. W witrynach programistycznych i małych zakładach produkcyjnych można ręcznie oceniać za pomocą poleceń systemu Linux lub narzędzi i bibliotek npm, takich jak inspektor węzłów i memwatch. Główną wadą tych ręcznych czynności jest to, że wymagają one aktywnego monitorowania przez człowieka - w przypadku poważnych zakładów produkcyjnych absolutnie niezbędne jest użycie solidnych narzędzi monitorowania, np. (AWS CloudWatch, DataDog lub inny podobny proaktywny system), który ostrzega o wystąpieniu wycieku. Istnieje również kilka wskazówek programistycznych, aby zapobiec wyciekom: unikaj przechowywania danych na poziomie globalnym, używaj strumieni danych o dynamicznym rozmiarze, ogranicz zakres zmiennych za pomocą let i const. From 07826eec70d88c2768b7de45c292292833829f0c Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:52:19 +0100 Subject: [PATCH 0230/1795] update monitoring.polish.md fixed --- sections/production/monitoring.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/monitoring.polish.md b/sections/production/monitoring.polish.md index 9ea979acd..4cb478e56 100644 --- a/sections/production/monitoring.polish.md +++ b/sections/production/monitoring.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Na bardzo podstawowym poziomie monitorowanie oznacza *łatwe* rozpoznanie, kiedy coś złego dzieje się na produkcji. Na przykład, otrzymując powiadomienie e-mailem lub Slack. Wyzwanie polega na wybraniu odpowiedniego zestawu narzędzi, które spełnią Twoje wymagania bez rozbijania banku. Mogę zasugerować, zacznij od zdefiniowania podstawowego zestawu wskaźników, które należy obserwować, aby zapewnić zdrowy stan - procesor, pamięć RAM serwera, pamięć RAM procesu węzła (mniej niż 1,4 GB), liczba błędów w ostatniej chwili, liczba ponownych uruchomień procesu, średni czas reakcji. Następnie zapoznaj się z zaawansowanymi funkcjami, które mogą ci się spodobać, i dodaj do swojej listy życzeń. Niektóre przykłady luksusowej funkcji monitorowania: profilowanie BD, pomiar między usługami (tj. mierzenie transakcji biznesowej), integracja frontendu, udostępnianie surowych danych niestandardowym klientom BI, powiadomienia Slack i wiele innych. From 4c64dd033876f32b2406c1a2b8c23f8e469ca694 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:52:41 +0100 Subject: [PATCH 0231/1795] update productioncode.polish.md fixed --- sections/production/productioncode.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/productioncode.polish.md b/sections/production/productioncode.polish.md index 8227e06f9..15a7b856d 100644 --- a/sections/production/productioncode.polish.md +++ b/sections/production/productioncode.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Poniżej znajduje się lista wskazówek programistycznych, które znacznie wpływają na utrzymanie produkcji i stabilność: From 6f2caf3fbf08a6c5cf57cf88a189443eccfca3bc Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:53:05 +0100 Subject: [PATCH 0232/1795] update setnodeenv.polish.md fixed --- sections/production/setnodeenv.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/setnodeenv.polish.md b/sections/production/setnodeenv.polish.md index 1f3e8da56..cdad19cdf 100644 --- a/sections/production/setnodeenv.polish.md +++ b/sections/production/setnodeenv.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Procesowe zmienne środowiskowe to zestaw par klucz-wartość udostępniony dowolnemu działającemu programowi, zwykle w celach konfiguracyjnych. Mimo że można używać dowolnych zmiennych, Node zachęca do korzystania ze zmiennej o nazwie NODE_ENV w celu oznaczenia, czy obecnie jesteśmy w produkcji. To określenie umożliwia komponentom lepszą diagnostykę podczas programowania, na przykład poprzez wyłączenie buforowania lub wysyłanie pełnych instrukcji dziennika. Każde nowoczesne narzędzie do wdrażania - Chef, Puppet, CloudFormation i inne - obsługuje ustawianie zmiennych środowiskowych podczas wdrażania From 78e534d43678119e9da992e4886642fb14c171e6 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:53:26 +0100 Subject: [PATCH 0233/1795] update smartlogging.polish.md fixed --- sections/production/smartlogging.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/smartlogging.polish.md b/sections/production/smartlogging.polish.md index e7e56ccf2..6c82faa98 100644 --- a/sections/production/smartlogging.polish.md +++ b/sections/production/smartlogging.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Ponieważ i tak drukujesz instrukcje dziennika i oczywiście potrzebujesz interfejsu, który zawija informacje produkcyjne, w których możesz śledzić błędy i podstawowe dane (np. ile błędów występuje co godzinę i który jest twój najwolniejszy punkt końcowy interfejsu API), dlaczego nie inwestować umiarkowanych wysiłków w solidne ramy rejestrowania, które zaznaczą wszystkie pola? Osiągnięcie tego wymaga przemyślanej decyzji w trzech krokach: From 243b38ec390051d915b234af62d846f7ba304b6f Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:53:52 +0100 Subject: [PATCH 0234/1795] update utilizecpu.polish.md fixed --- sections/production/utilizecpu.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/utilizecpu.polish.md b/sections/production/utilizecpu.polish.md index 14c9f30e0..c75984962 100644 --- a/sections/production/utilizecpu.polish.md +++ b/sections/production/utilizecpu.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Nic dziwnego, że w swojej podstawowej formie Node działa na jednym wątku = pojedynczy proces = pojedynczy procesor. Płacenie za mocny sprzęt z 4 lub 8 procesorami i używanie tylko jednego brzmi szalenie, prawda? Najszybszym rozwiązaniem, które pasuje do średnich aplikacji jest użycie modułu klastrowania Node, który w 10 liniach kodu tworzy proces dla każdego logicznego rdzenia i kieruje żądania między procesami w stylu round-robin. Jeszcze lepiej, użyj PM2, który otacza moduł klastrowania prostym interfejsem i fajnym interfejsem monitorowania. Chociaż to rozwiązanie działa dobrze w przypadku tradycyjnych aplikacji, może nie być wystarczające w przypadku aplikacji, które wymagają najwyższej wydajności i niezawodnego przepływu DevOps. W przypadku zaawansowanych przypadków użycia rozważ replikację procesu NODE przy użyciu niestandardowego skryptu wdrażania i równoważenie za pomocą specjalistycznego narzędzia, takiego jak nginx, lub użyj silnika kontenera, takiego jak AWS ECS lub Kubernetees, które mają zaawansowane funkcje wdrażania i replikacji procesów. From 0b9be054b0778c84bb147fd63a7be684bab216e1 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:54:31 +0100 Subject: [PATCH 0235/1795] update breakintcomponents.polish.md fixed --- sections/projectstructre/breakintcomponents.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/breakintcomponents.polish.md b/sections/projectstructre/breakintcomponents.polish.md index 582f48a98..c9650bc21 100644 --- a/sections/projectstructre/breakintcomponents.polish.md +++ b/sections/projectstructre/breakintcomponents.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem W przypadku średnich aplikacji i wyższych monolity są naprawdę złe - posiadanie jednego dużego oprogramowania z wieloma zależnościami jest po prostu trudne do uzasadnienia i często prowadzi do kodu spaghetti. Nawet inteligentni architekci - ci, którzy są wystarczająco wykwalifikowani, aby oswoić bestię i ją „zmodularyzować” - poświęcają wielki wysiłek umysłowy na projektowanie, a każda zmiana wymaga starannej oceny wpływu na inne zależne obiekty. Ostatecznym rozwiązaniem jest opracowanie małego oprogramowania: podziel cały stos na niezależne komponenty, które nie współużytkują plików z innymi, każdy stanowi bardzo niewiele plików (np. API, usługa, dostęp do danych, test itp.), Dzięki czemu jest bardzo łatwo to uzasadnić. Niektórzy mogą nazywać tę architekturę „mikrousługami” - ważne jest, aby zrozumieć, że mikrousługi nie są specyfikacją, której należy przestrzegać, ale raczej zbiorem zasad. Możesz zastosować wiele zasad w pełnej architekturze mikrousług lub zastosować tylko kilka. Oba są dobre, o ile złożoność oprogramowania jest niska. To co, co najmniej powinieneś zrobić, to utworzyć podstawowe granice między komponentami, przypisać folder w katalogu głównym projektu dla każdego komponentu biznesowego i uczynić go samodzielnym - inne komponenty mogą korzystać z jego funkcji tylko za pośrednictwem publicznego interfejsu lub interfejsu API. Jest to podstawa do uproszczenia komponentów, uniknięcia piekła zależności i utorowania drogi do pełnej obsługi mikrousług w przyszłości, gdy Twoja aplikacja się rozwinie.

From 4b2c199f9198ea158e78cafee8a055ee547e4db3 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:54:53 +0100 Subject: [PATCH 0236/1795] update configguide.polish.md fixed --- sections/projectstructre/configguide.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/configguide.polish.md b/sections/projectstructre/configguide.polish.md index b5fd11960..d3d9bd7b4 100644 --- a/sections/projectstructre/configguide.polish.md +++ b/sections/projectstructre/configguide.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem W przypadku danych konfiguracyjnych wiele rzeczy może po prostu denerwować i spowalniać: From 6e1343a715343504f0c3dc866bb386d476c66906 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:55:23 +0100 Subject: [PATCH 0237/1795] update separateexpress.polish.md fixed --- sections/projectstructre/separateexpress.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/separateexpress.polish.md b/sections/projectstructre/separateexpress.polish.md index 8ac09f55c..77cba2980 100644 --- a/sections/projectstructre/separateexpress.polish.md +++ b/sections/projectstructre/separateexpress.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Najnowszy generator Express ma świetną praktykę, którą warto zachować - deklaracja API jest oddzielona od konfiguracji związanej z siecią (port, protokół itp.). Umożliwia to testowanie interfejsu API w trakcie procesu, bez wykonywania połączeń sieciowych, ze wszystkimi korzyściami, które przynosi do tabeli: szybkie wykonywanie testów i uzyskiwanie wskaźników zasięgu kodu. Pozwala także na wdrożenie tego samego interfejsu API w elastycznych i różnych warunkach sieciowych. Bonus: lepsze rozdzielenie problemów i czystszy kod From f2eded46ac086a4318951c9254c3710f11de1cd4 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:55:48 +0100 Subject: [PATCH 0238/1795] update wraputilities.polish.md fixed --- sections/projectstructre/wraputilities.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/wraputilities.polish.md b/sections/projectstructre/wraputilities.polish.md index b792f7553..42470bd36 100644 --- a/sections/projectstructre/wraputilities.polish.md +++ b/sections/projectstructre/wraputilities.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Kiedy zaczniesz się rozwijać i będziesz mieć różne komponenty na różnych serwerach, które zużywają podobne narzędzia, powinieneś zacząć zarządzać zależnościami - w jaki sposób możesz zachować 1 kopię kodu narzędzia i pozwolić, aby wiele komponentów konsumenckich używało go i wdrażało? Cóż, istnieje narzędzie do tego, nazywa się npm... Zacznij od zawinięcia pakietów narzędziowych stron trzecich własnym kodem, aby w przyszłości można go łatwo było wymienić i opublikować własny kod jako prywatny pakiet npm. Teraz cała baza kodu może zaimportować ten kod i bezpłatne narzędzie do zarządzania zależnościami. Możliwe jest publikowanie pakietów NPM na własny użytek bez publicznego udostępniania go za pomocą [prywatnych modułów](https://docs.npmjs.com/private-modules/intro), [prywatnego rejestru](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html) lub [lokalnych pakietów npm](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc) From a7ff3943b7e7787e430be1d16246424f001e2ee7 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:56:11 +0100 Subject: [PATCH 0239/1795] update refactoring.polish.md fixed --- sections/testingandquality/refactoring.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/refactoring.polish.md b/sections/testingandquality/refactoring.polish.md index 99121e2f2..1bba53299 100644 --- a/sections/testingandquality/refactoring.polish.md +++ b/sections/testingandquality/refactoring.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Refaktoryzacja jest ważnym procesem w iteracyjnym przepływie rozwoju. Usunięcie „Code smells” (złych praktyk kodowania), takich jak powielony kod, długie metody, długa lista parametrów, poprawi kod i sprawi, że będzie on łatwiejszy w utrzymaniu. Korzystanie z narzędzi do analizy statycznej pomoże ci znaleźć te code smells i zbudować proces wokół refaktoryzacji. Dodanie tych narzędzi do kompilacji CI pomoże zautomatyzować proces kontroli jakości. Jeśli Twój CI zintegruje się z narzędziem takim jak Sonar lub Code Climate, kompilacja zakończy się niepowodzeniem, jeśli wykryje code smells i poinformuje autora, jak rozwiązać problem. Te narzędzia do analizy statycznej uzupełnią narzędzia do lintowania, takie jak ESLint. Większość narzędzi do lintowania skupia się na stylach kodu, takich jak wcięcia i brakujące średniki (chociaż niektóre code smells kodu jak Długie funkcje) w jednym pliku, podczas gdy narzędzia analizy statycznej skoncentrują się na wyszukiwaniu code smells (duplikat kodu, analiza złożoności itp.) pojedyncze pliki i wiele plików. From 585e0383f644aeea1b4cbda795dcc365e14c52a2 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:56:34 +0100 Subject: [PATCH 0240/1795] update citools.polish.md fixed --- sections/testingandquality/citools.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/citools.polish.md b/sections/testingandquality/citools.polish.md index 32dd53859..cefaa3dd3 100644 --- a/sections/testingandquality/citools.polish.md +++ b/sections/testingandquality/citools.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Świat CI był kiedyś elastyczny [Jenkins](https://jenkins.io/) vs prostota dostawców SaaS. Gra się teraz zmienia, ponieważ dostawcy SaaS, tacy jak [CircleCI](https://circleci.com/) i [Travis](https://travis-ci.org/), oferują solidne rozwiązania, w tym kontenery Docker z minimalnym czasem instalacji, podczas gdy Jenkins stara się także konkurować w segmencie „prostoty”. Chociaż można skonfigurować bogate rozwiązanie CI w chmurze, to gdyby wymagało to kontrolowania najdrobniejszych szczegółów, Jenkins jest nadal preferowaną platformą. Wybór ostatecznie sprowadza się do tego, w jakim stopniu proces CI powinien być dostosowany: darmowi i konfigurujący dostawcy darmowej chmury pozwalają na uruchamianie niestandardowych poleceń powłoki, niestandardowych obrazów dokerów, dostosowywanie przepływu pracy, uruchamianie kompilacji macierzy i innych bogatych funkcji. Jeśli jednak konieczne jest kontrolowanie infrastruktury lub programowanie logiki CI za pomocą formalnego języka programowania, takiego jak Java, Jenkins może nadal być wyborem. W przeciwnym razie rozważ wybranie prostej i skonfiguruj opcję bezpłatnej chmury From b6ba7d338f62d62d1388a118dfe8ae65d42c6150 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:56:57 +0100 Subject: [PATCH 0241/1795] update avoid-global-test-fixture.polish.md fixed --- sections/testingandquality/avoid-global-test-fixture.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/avoid-global-test-fixture.polish.md b/sections/testingandquality/avoid-global-test-fixture.polish.md index 53a1c4c0a..43791fb7a 100644 --- a/sections/testingandquality/avoid-global-test-fixture.polish.md +++ b/sections/testingandquality/avoid-global-test-fixture.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Kierując się złotą zasadą testowania - spraw, aby przypadki testowe były wyjątkowo proste, każdy test powinien dodawać i działać na swoim własnym zestawie wierszy BD, aby zapobiec sprzężeniu i łatwo uzasadnić przebieg testu. W rzeczywistości jest to często naruszane przez testerów, którzy zapełniają bazę danych danymi przed uruchomieniem testów (znanych również jako „urządzenie testowe”) w celu poprawy wydajności. Chociaż wydajność jest istotnym problemem - można ją złagodzić (np. BD w pamięci, patrz punkt „Testowanie komponentów”), jednak złożoność testu jest bardzo bolesnym smutkiem, który powinien rządzić innymi rozważaniami. Praktycznie spraw, aby każdy przypadek testowy wyraźnie dodał potrzebne rekordy BD i działał tylko na tych rekordach. Jeśli wydajność stanie się kluczowym problemem - zrównoważony kompromis może przyjść w postaci inicjowania jedynego zestawu testów, które nie powodują mutacji danych (np. zapytania) From b1ee07074537f905fa6e6da83974f434cbc783b9 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:57:18 +0100 Subject: [PATCH 0242/1795] update aaa.polish.md fixed --- sections/testingandquality/aaa.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/aaa.polish.md b/sections/testingandquality/aaa.polish.md index 3cefb1ff8..6e39c20e3 100644 --- a/sections/testingandquality/aaa.polish.md +++ b/sections/testingandquality/aaa.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Naszym największym wyzwaniem do testowania jest brak przestrzeni nad głową - już mamy kod produkcyjny, który sprawia, że jesteśmy bardzo zajęci. Z tego powodu kod testowy musi pozostać śmiertelnie prosty i łatwy do zrozumienia. Podczas czytania przypadku testowego - nie powinno to przypominać czytania kodu imperatywnego (pętli, dziedziczenia), a raczej HTML - deklaratywne doświadczenie. Aby to osiągnąć, zachowaj konwencję AAA, aby czytelnicy mogli bez wysiłku przeanalizować cel testu. Istnieją inne podobne formaty tego wzorca, takie jak XUnit „Setup, Excercise, Verify, Teardown”. Oto trzy A: Pierwsze A - Arrange: Cały kod instalacyjny, aby wprowadzić system do scenariusza, który test ma na celu symulację. Może to obejmować tworzenie instancji testowanego konstruktora, dodawanie rekordów BD, mockowanie/stubbing obiektów i dowolny inny kod przygotowawczy From dc1b5737b4773c93b2bcba7adeb33dacd2fca598 Mon Sep 17 00:00:00 2001 From: Michal Date: Thu, 26 Mar 2020 17:57:46 +0100 Subject: [PATCH 0243/1795] update 3-parts-in-name.polish.md fixed --- sections/testingandquality/3-parts-in-name.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/3-parts-in-name.polish.md b/sections/testingandquality/3-parts-in-name.polish.md index c8ef2326c..d5b0a3572 100644 --- a/sections/testingandquality/3-parts-in-name.polish.md +++ b/sections/testingandquality/3-parts-in-name.polish.md @@ -2,7 +2,7 @@

-### Wyjaśnienie jednego akapitu +### Wyjaśnienie jednym akapitem Raport z testu powinien informować, czy bieżąca wersja aplikacji spełnia wymagania osób, które niekoniecznie znają kod: testera, wdrażającego program DevOps i przyszłego ciebie za dwa lata. Można to najlepiej osiągnąć, jeśli testy mówią na poziomie wymagań i obejmują 3 części: From 70466e00703f8c02644c211e7b16b5949835f0fe Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:43:01 +0000 Subject: [PATCH 0244/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfece7356..56f1a667f 100644 --- a/README.md +++ b/README.md @@ -1297,6 +1297,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
VinayaSathyanarayana

🖋
Kim Kern

🖋
Kenneth Freitas

🖋 +
songe

🖋 From 89afed1856c3b8904a627206743d861e372aafe9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:43:02 +0000 Subject: [PATCH 0245/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e3f06e8a5..182bb4d21 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -869,6 +869,15 @@ "contributions": [ "content" ] + }, + { + "login": "songe", + "name": "songe", + "avatar_url": "https://avatars2.githubusercontent.com/u/1531561?v=4", + "profile": "https://github.com/songe", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 285f236b87d2810254667e01ac94adfa75bed8b3 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 30 Mar 2020 09:45:27 +0300 Subject: [PATCH 0246/1795] Update operations-manual.md --- .operations/operations-manual.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.operations/operations-manual.md b/.operations/operations-manual.md index a161b9353..a98efa50c 100644 --- a/.operations/operations-manual.md +++ b/.operations/operations-manual.md @@ -80,9 +80,10 @@ Each month, a maintainer on call will open an issue for maintenance work and rec | French | Yoni | | Russian | Yoni | | Korean | Yoni | -| Spanish | Kyle | +| Spanish | Kevyn | | Chinese | Yoni | | Korean | Kyle | | Egyptian | Yoni | | Ukrainian | Bruno | +| Polish. | Kevyn | From 9307478f9cf017dff6df4641341e46d239398a3f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:48:55 +0000 Subject: [PATCH 0247/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfece7356..40cd10087 100644 --- a/README.md +++ b/README.md @@ -1297,6 +1297,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
VinayaSathyanarayana

🖋
Kim Kern

🖋
Kenneth Freitas

🖋 +
Albert

🖋 From 6074d0b617d1e45d4fb07e28083160ba8bc0c1e6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:48:56 +0000 Subject: [PATCH 0248/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e3f06e8a5..5cb8f3655 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -869,6 +869,15 @@ "contributions": [ "content" ] + }, + { + "login": "AlbertRAR", + "name": "Albert", + "avatar_url": "https://avatars2.githubusercontent.com/u/6898522?v=4", + "profile": "https://github.com/AlbertRAR", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 2f2572ddfbdff1ab5379235e543aed32e9ed18be Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:54:46 +0000 Subject: [PATCH 0249/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dfece7356..cd7391034 100644 --- a/README.md +++ b/README.md @@ -1297,6 +1297,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
VinayaSathyanarayana

🖋
Kim Kern

🖋
Kenneth Freitas

🖋 +
Michal

🌍 🖋 From f8ecdd8baadd1153be335f2012c4481fb4892e69 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 06:54:47 +0000 Subject: [PATCH 0250/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e3f06e8a5..aad11b164 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -869,6 +869,16 @@ "contributions": [ "content" ] + }, + { + "login": "mbiesiad", + "name": "Michal", + "avatar_url": "https://avatars0.githubusercontent.com/u/18367606?v=4", + "profile": "http://c0dingp0int3r.design.blog", + "contributions": [ + "translation", + "content" + ] } ], "projectName": "nodebestpractices", From 1a4a2e1dd370bc7753aa8dd32164003f25ea1881 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 30 Mar 2020 10:12:20 +0300 Subject: [PATCH 0251/1795] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2af73ce6c..56f1a667f 100644 --- a/README.md +++ b/README.md @@ -1297,7 +1297,6 @@ Thanks goes to these wonderful people who have contributed to this repository!
VinayaSathyanarayana

🖋
Kim Kern

🖋
Kenneth Freitas

🖋 -
songe

🖋 From e18fdd94e8a1b54d9c10882ccc4387ab5435a24a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 07:16:02 +0000 Subject: [PATCH 0252/1795] docs: update README.md [skip ci] From 47ee79c596e0281ccd4febc13f3ce56ffec760f2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2020 07:16:03 +0000 Subject: [PATCH 0253/1795] docs: update .all-contributorsrc [skip ci] From 9efa4739725181dac6b648bc46e93d88b4f5bc3c Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 16:30:17 +0200 Subject: [PATCH 0254/1795] update README.md deleted Polish from lang. info --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f644722bd..509c32671 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@
-Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md), [![PL](/assets/flags/PL.png)**PL**](/README.polish.md), [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) +Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations)
From bc0ab8c52bf4757cce6ae42708c5d7f101da3c08 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 17:59:27 +0200 Subject: [PATCH 0255/1795] update README.polish.md fixed internal link --- README.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.polish.md b/README.polish.md index 19147e489..1b4b39569 100644 --- a/README.polish.md +++ b/README.polish.md @@ -22,7 +22,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM
-###### Zbudowane i utrzymywane przez nasz [Steering Committee](#steering-committee) oraz [Collaborators](#collaborators) +###### Zbudowane i utrzymywane przez nasz [Steering Committee](#steering-committee) oraz [Collaborators](#współpracownicy) # Najnowsze najlepsze praktyki i aktualności From 48f891036257ed0341397899c105a773c5c4ff29 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 18:03:55 +0200 Subject: [PATCH 0256/1795] update README.polish.md fixed internal link (translations-tlumaczenia) --- README.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.polish.md b/README.polish.md index 1b4b39569..44797dc4f 100644 --- a/README.polish.md +++ b/README.polish.md @@ -18,7 +18,7 @@
-Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** i ![TR](/assets/flags/TR.png)**TR** w trakcie!)](#translations) +Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** i ![TR](/assets/flags/TR.png)**TR** w trakcie!)](#tłumaczenia)
From 5f84cd82fe72bb0c61de7d488dd85a5acff5fd8d Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 18:29:19 +0200 Subject: [PATCH 0257/1795] update README.polish.md small fix (new line) --- README.polish.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.polish.md b/README.polish.md index 44797dc4f..d3311e9d5 100644 --- a/README.polish.md +++ b/README.polish.md @@ -37,6 +37,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM # Witamy! 3 rzeczy, które musisz wiedzieć na początku **1. W rzeczywistości czytasz dziesiątki najlepszych artykułów na temat Node.js -** to repozytorium jest podsumowaniem i zbiorem najlepszych pozycji na temat najlepszych praktyk Node.js, a także treści napisanych tutaj przez współpracowników + **2. Jest to największa kompilacja, która rośnie z każdym tygodniem -** obecnie prezentowanych jest ponad 80 najlepszych praktyk, przewodników po stylach i wskazówek architektonicznych. Nowe wydania i pull requesty są tworzone codziennie, aby aktualizować tę książkę na żywo. Chcielibyśmy zobaczyć Twój wkład w to, czy naprawiasz błędy w kodzie, pomagasz w tłumaczeniach, czy sugerujesz nowe genialne pomysły. Zobacz nasze [wskazówki dotyczące pisania tutaj](https://github.com/mbiesiad/nodebestpractices/blob/master/.operations/writing-guidelines.polish.md) **3. Większość najlepszych praktyk ma dodatkowe informacje -** większość pocisków zawiera link **🔗Przeczytaj więcej**, który rozszerza praktykę o przykłady kodu, cytaty z wybranych blogów i więcej informacji From 867574d8fdebc0db32192435fee6fe72f21f0524 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 18:35:24 +0200 Subject: [PATCH 0258/1795] update README.polish.md small fixes - new lines --- README.polish.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.polish.md b/README.polish.md index d3311e9d5..cc35dcb97 100644 --- a/README.polish.md +++ b/README.polish.md @@ -80,6 +80,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 1.3 Opakuj typowe narzędzia jako pakiety npm **TL;DR:** W dużej aplikacji, która stanowi dużą bazę kodu, kluczowe narzędzia, takie jak rejestrator, szyfrowanie i podobne, powinny być owinięte własnym kodem i udostępnione jako prywatne pakiety npm. Pozwala to na dzielenie się nimi między wieloma bazami kodów i projektami + **W przeciwnym razie:** Będziesz musiał wymyślić własne koło wdrażania i zależności 🔗 [**Czytaj więcej: Struktura według funkcji**](/sections/projectstructre/wraputilities.polish.md) @@ -89,6 +90,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 1.4 Oddzielna „aplikacja” i „serwer” Express **TL;DR:** Unikaj nieprzyjemnego nawyku definiowania całości [Express](https://expressjs.com/) aplikacja w jednym dużym pliku - rozdziel definicję „Express” na co najmniej dwa pliki: deklarację API (app.js) i problemy z siecią (WWW). Aby uzyskać jeszcze lepszą strukturę, znajdź deklarację API w komponentach + **W przeciwnym razie:** Twój interfejs API będzie dostępny do testowania tylko za pośrednictwem połączeń HTTP (wolniejsze i znacznie trudniejsze do generowania raportów zasięgu). Utrzymanie setek linii kodu w jednym pliku prawdopodobnie nie będzie wielką przyjemnością 🔗 [**Czytaj więcej: oddzielna aplikacja „Express” i „serwer”**](/sections/projectstructre/separateexpress.polish.md) @@ -98,6 +100,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 1.5 Używaj konfiguracji przyjaznej środowisku, bezpiecznej i hierarchicznej **TL;DR:** Idealne i bezbłędne ustawienie konfiguracji powinno zapewnić, że (a) klucze można odczytać z pliku ORAZ ze zmiennych środowiskowych (b) dane wrażliwe są przechowywane poza zatwierdzonym kodem (c) konfiguracja jest hierarchiczna dla łatwiejszego wyszukiwania. Istnieje kilka pakietów, które mogą pomóc zaznaczyć większość z tych pól, takich jak [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf ) i [config](https://www.npmjs.com/package/config) + **W przeciwnym razie:** Niespełnienie któregokolwiek z wymagań konfiguracji po prostu ugrzęźnie w zespole programistów lub DevOps. Prawdopodobnie jedno i drugie 🔗 [**Czytaj więcej: najlepsze praktyki dotyczące konfiguracji**](/sections/projectstructre/configguide.polish.md) @@ -111,6 +114,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 2.1 Użyj Async-Await lub promises do obsługi błędów asynchronicznych **TL;DR:** Obsługa błędów asynchronicznych w stylu wywołania zwrotnego jest prawdopodobnie najszybszą drogą do piekła (znane też jako Piramida zagłady). Najlepszy prezent, jaki możesz dać kodowi, to skorzystanie z renomowanej biblioteki promise lub async-await zamiast tego, co umożliwia znacznie bardziej zwartą i znaną składnię kodu, taką jak try-catch + **W przeciwnym razie:** styl wywołania zwrotnego Node.js, funkcja (błąd, odpowiedź) jest obiecującym sposobem na niemożliwy do utrzymania kod ze względu na połączenie obsługi błędów z przypadkowym kodem, nadmiernym zagnieżdżaniem i niewygodnymi wzorcami kodowania 🔗 [**Czytaj więcej: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.polish.md) From dd210b7981348ab2786d68538f067602820b0678 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 18:50:02 +0200 Subject: [PATCH 0259/1795] update README.polish.md small fixes --- README.polish.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.polish.md b/README.polish.md index cc35dcb97..325649bff 100644 --- a/README.polish.md +++ b/README.polish.md @@ -89,7 +89,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 1.4 Oddzielna „aplikacja” i „serwer” Express -**TL;DR:** Unikaj nieprzyjemnego nawyku definiowania całości [Express](https://expressjs.com/) aplikacja w jednym dużym pliku - rozdziel definicję „Express” na co najmniej dwa pliki: deklarację API (app.js) i problemy z siecią (WWW). Aby uzyskać jeszcze lepszą strukturę, znajdź deklarację API w komponentach +**TL;DR:** Unikaj nieprzyjemnego nawyku definiowania całości aplikacji [Express](https://expressjs.com/) w jednym dużym pliku - rozdziel definicję „Express” na co najmniej dwa pliki: deklarację API (app.js) i problemy z siecią (WWW). Aby uzyskać jeszcze lepszą strukturę, znajdź deklarację API w komponentach **W przeciwnym razie:** Twój interfejs API będzie dostępny do testowania tylko za pośrednictwem połączeń HTTP (wolniejsze i znacznie trudniejsze do generowania raportów zasięgu). Utrzymanie setek linii kodu w jednym pliku prawdopodobnie nie będzie wielką przyjemnością @@ -521,7 +521,7 @@ Wszystkie powyższe instrukcje zwrócą wartość false, jeśli zostaną użyte ## ![✔] 4.11 Refaktoryzuj regularnie za pomocą narzędzi do analizy statycznej -**TL;DR:** Korzystanie z narzędzi analizy statycznej pomaga, zapewniając obiektywne sposoby poprawy jakości kodu i utrzymując kod w łatwości konserwacji. Możesz dodać narzędzia analizy statycznej do kompilacji CI, aby zawieść, gdy wykryje code smells. Jego głównymi zaletami w stosunku do zwykłego szarpania jest możliwość kontroli jakości w kontekście wielu plików (np. wykrywanie duplikacji), przeprowadzania zaawansowanej analizy (np. złożoności kodu) oraz śledzenia historii i postępu problemów z kodem. Dwa przykłady narzędzi, których możesz użyć, to [Sonarqube](https://www.sonarqube.org/) (2600+ [gwiazdek](https://github.com/SonarSource/sonarqube)) i [Code Climate](https : //codeclimate.com/) (1500+ [gwiazdek](https://github.com/codeclimate/codeclimate)). +**TL;DR:** Korzystanie z narzędzi analizy statycznej pomaga, zapewniając obiektywne sposoby poprawy jakości kodu i utrzymując kod w łatwości konserwacji. Możesz dodać narzędzia analizy statycznej do kompilacji CI, aby zawieść, gdy wykryje code smells. Jego głównymi zaletami w stosunku do zwykłego szarpania jest możliwość kontroli jakości w kontekście wielu plików (np. wykrywanie duplikacji), przeprowadzania zaawansowanej analizy (np. złożoności kodu) oraz śledzenia historii i postępu problemów z kodem. Dwa przykłady narzędzi, których możesz użyć, to [Sonarqube](https://www.sonarqube.org/) (2600+ [gwiazdek](https://github.com/SonarSource/sonarqube)) i [Code Climate](https://codeclimate.com/) (1500+ [gwiazdek](https://github.com/codeclimate/codeclimate)). **W przeciwnym razie:** Przy złej jakości kodu błędy i wydajność zawsze będą stanowić problem, którego nie będzie w stanie naprawić żadna nowa błyszcząca biblioteka ani najnowocześniejsze funkcje From 47b3bd6502284ead138260facb310e463e65f1a9 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:07:34 +0200 Subject: [PATCH 0260/1795] update block-loop.polish.md little fix, improved --- sections/performance/block-loop.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/performance/block-loop.polish.md b/sections/performance/block-loop.polish.md index d69365090..401677ac5 100644 --- a/sections/performance/block-loop.polish.md +++ b/sections/performance/block-loop.polish.md @@ -5,7 +5,7 @@ Node obsługuje pętlę zdarzeń głównie w jednym wątku obracającym się przez wiele kolejek. Operacje o wysokim stopniu złożoności, dużym parsowaniu jsonów, stosowaniu logiki na wielkich tablicach, niebezpiecznych zapytaniach regularnych i dużych operacjach We / Wy to niektóre z operacji, które mogą powodować zawieszanie się pętli zdarzeń. Unikaj odciążania zadań intensywnie wykorzystujących procesor do dedykowanej usługi (np. serwera zadań) lub dzielenia długich zadań na małe kroki, a następnie używanie puli pracowników to kilka przykładów tego, jak uniknąć blokowania pętli zdarzeń. ### Przykład: blokowanie pętli zdarzeń -Let's take a look at an example from [Node Clinic](https://clinicjs.org/documentation/doctor/05-fixing-event-loop-problem). +Spójrzmy na przykład z [Node Clinic](https://clinicjs.org/documentation/doctor/05-fixing-event-loop-problem). ```javascript function sleep (ms) { const future = Date.now() + ms From dcd1f68a517d2aa35d0ebc9364b4caec68a119d0 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:11:14 +0200 Subject: [PATCH 0261/1795] update logrouting.polish.md little fix, improved --- sections/production/logrouting.polish.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/production/logrouting.polish.md b/sections/production/logrouting.polish.md index 0b2a278e3..fb0509d8b 100644 --- a/sections/production/logrouting.polish.md +++ b/sections/production/logrouting.polish.md @@ -38,7 +38,7 @@ Robiąc to w ten sposób, aplikacja obsługuje teraz zarówno logikę aplikacji

### Przykład kodu - Lepsza obsługa dziennika + przykład Docker -In the application: +W aplikacji: ```javascript const logger = new winston.Logger({ level: 'info', @@ -49,7 +49,7 @@ const logger = new winston.Logger({ logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); ``` -Then, in the docker container `daemon.json`: +Następnie, w kontenerze dockera `daemon.json`: ```json5 { "log-driver": "splunk", // just using Splunk as an example, it could be another storage type From cfad99c0f6523991f046c866a69e097bca714be9 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:15:42 +0200 Subject: [PATCH 0262/1795] update expirejwt.polish.md small fix, improved --- sections/security/expirejwt.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/expirejwt.polish.md b/sections/security/expirejwt.polish.md index 32335c7ec..ce23f0a01 100644 --- a/sections/security/expirejwt.polish.md +++ b/sections/security/expirejwt.polish.md @@ -40,5 +40,5 @@ app.get('/logout', (req, res) => { ### Co mówią inni blogerzy -From the blog by [Marc Busqué](http://waiting-for-dev.github.io/blog/2017/01/25/jwt_secure_usage/): +Z bloga od [Marc Busqué](http://waiting-for-dev.github.io/blog/2017/01/25/jwt_secure_usage/): > ...add a revocation layer on top of JWT, even if it implies losing its stateless nature. From 96bed5ca62fbee6cfc7e69a473ab300c78e9ad9d Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:17:02 +0200 Subject: [PATCH 0263/1795] update non-root-user.polish.md fixed, just a typo --- sections/security/non-root-user.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/non-root-user.polish.md b/sections/security/non-root-user.polish.md index 6ad823c6c..c77fc91b2 100644 --- a/sections/security/non-root-user.polish.md +++ b/sections/security/non-root-user.polish.md @@ -37,5 +37,5 @@ Z bloga Don't run Node.js as root od [Olivier Lalonde](http://syskall.com/dont-r ### Cytat z Blogu: "If you need to run your application on port 80 or 443, you can do port forwarding" -Z blogu Developing Secure Node.js Applications — A Broad Guide by [Deepal Jayasekara](https://jsblog.insiderattack.net/developing-secure-node-js-applications-a-broad-guide-286afdec69ce): +Z bloga Developing Secure Node.js Applications — A Broad Guide by [Deepal Jayasekara](https://jsblog.insiderattack.net/developing-secure-node-js-applications-a-broad-guide-286afdec69ce): > Never run Node.js as root. Running node.js as root will make it worse if an attacker somehow gains control over your application. In this scenario, attacker would also gain root privileges which could result in a catastrophe. If you need to run your application on port 80 or 443, you can do port forwarding using iptables or you can place a front-end proxy such as nginx or apache which routes request from port 80 or 443 to your application From 063ef4c68a1e8a70295258e1127e341cd915828f Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:19:43 +0200 Subject: [PATCH 0264/1795] update sandbox.polish.md fixed, missing space key --- sections/security/sandbox.polish.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/security/sandbox.polish.md b/sections/security/sandbox.polish.md index 5c21f2b3d..7ba6e04ac 100644 --- a/sections/security/sandbox.polish.md +++ b/sections/security/sandbox.polish.md @@ -1,4 +1,4 @@ -#Uruchom niebezpieczny kod w piaskownicy +# Uruchom niebezpieczny kod w piaskownicy ### Wyjaśnienie jednym akapitem From 5b39234e300477064d88582a755276062df741b8 Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 30 Mar 2020 19:24:17 +0200 Subject: [PATCH 0265/1795] update secureheaders.polish.md fixed internal links --- sections/security/secureheaders.polish.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/security/secureheaders.polish.md b/sections/security/secureheaders.polish.md index a1b7a8683..247d4a8c7 100644 --- a/sections/security/secureheaders.polish.md +++ b/sections/security/secureheaders.polish.md @@ -11,14 +11,14 @@ Istnieją nagłówki związane z bezpieczeństwem, które służą do dalszego z ### Spis treści - [HTTP Strict Transport Security (HSTS)](#http-strict-transport-security-hsts) -- [Public Key Pinning for HTTP (HPKP)](#public-key-pinning-for-http-hpkp) +- [Public Key Pinning dla HTTP (HPKP)](#public-key-pinning-dla-http-hpkp) - [X-Frame-Options](#x-frame-options) - [X-XSS-Protection](#x-xss-protection) - [X-Content-Type-Options](#x-content-type-options) - [Referrer-Policy](#referrer-policy) - [Expect-CT](#expect-ct) - [Content-Security-Policy](#content-security-policy) -- [Additional Resource](#additional-resources) +- [Additional Resource](#dodatkowe-zasoby)

From 6ca183403b4a05334d54a41d4d2dc0c8e2010ccf Mon Sep 17 00:00:00 2001 From: MINDOCK Date: Tue, 24 Mar 2020 02:33:11 +0900 Subject: [PATCH 0266/1795] Translate read more(Using ESLint and Prettier) of section 3.1 --- .../eslint_prettier.korean.md | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 sections/codestylepractices/eslint_prettier.korean.md diff --git a/sections/codestylepractices/eslint_prettier.korean.md b/sections/codestylepractices/eslint_prettier.korean.md new file mode 100644 index 000000000..698de15c6 --- /dev/null +++ b/sections/codestylepractices/eslint_prettier.korean.md @@ -0,0 +1,25 @@ +# ESLint와 Prettier 사용하기 + +### ESLint와 Prettier 비교하기 + +만약 당신이 ESLint를 사용해서 이 코드를 포맷한다면, 당신에게 너무 넓다는 경고만 줄 것이다 (당신의 `mas-len` 설정에 의존해). Prettier는 자동적으로 당신을 위해 그것을 포맷할 것이다. + +```javascript +foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne(), noWayYouGottaBeKiddingMe()); +``` + +```javascript +foo( + reallyLongArg(), + omgSoManyParameters(), + IShouldRefactorThis(), + isThereSeriouslyAnotherOne(), + noWayYouGottaBeKiddingMe() +); +``` + +출처: [https://github.com/prettier/prettier-eslint/issues/101](https://github.com/prettier/prettier-eslint/issues/101) + +### ESLint와 Prettier 통합시키기 + +ESLint와 Prettier는 코드 포맷팅 기능에서 겹치지만 [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), 그리고 [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier)와 같은 다른 패키지들을 사용해 쉽게 결합시킬 수 있다. 그들의 차이점에 대해 더 많은 내용을 보려면, 당신은 [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint) 링크를 볼 수 있다. From 8f6ee4d1a8678def770c9a953f6cb311d2253db2 Mon Sep 17 00:00:00 2001 From: "A.Song" Date: Mon, 30 Mar 2020 12:29:10 -0700 Subject: [PATCH 0267/1795] Add Collaborators --- README.korean.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/README.korean.md b/README.korean.md index c88304dfb..1b5c34f68 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1122,7 +1122,7 @@ Our collaborators are members who are contributing to the repository on a regula | :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | | [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | -### Past collaborators +### 구 협력자 | | | :-------------------------------------------------------------------------------------------------------------------------: | @@ -1131,11 +1131,11 @@ Our collaborators are members who are contributing to the repository on a regula
## Contributing -If you've ever wanted to contribute to open source, now is your chance! See the [contributing docs](.operations/CONTRIBUTING.md) for more information. +오픈소스에 참여하고 싶으시다면 지금이 바로 기회! [contributing docs](.operations/CONTRIBUTING.md)에서 더 자세한 내용을 확인하세요. -## Contributors ✨ +## 기여자 ✨ -Thanks goes to these wonderful people who have contributed to this repository! +이 리포지터리에 기여해 주신 여기 이 모든 분께 감사드립니다! @@ -1262,6 +1262,9 @@ Thanks goes to these wonderful people who have contributed to this repository!
Hafez

🖋
Chandiran

🖋
VinayaSathyanarayana

🖋 +
Kim Kern

🖋 +
Kenneth Freitas

🖋 +
songe

🖋 From 2b329915d22678f24e889b26f73ad95308dd9bdc Mon Sep 17 00:00:00 2001 From: "A.Song" Date: Sun, 5 Apr 2020 00:51:05 -0700 Subject: [PATCH 0268/1795] Translate README.korean.md - Update intro & other sections to sound more natural in Korean - Fix typos - Add the missing code example in section 3.4 - Translate sections 6 & 7 --- README.korean.md | 421 +++++++++++++++++++++++++---------------------- 1 file changed, 227 insertions(+), 194 deletions(-) diff --git a/README.korean.md b/README.korean.md index 1b5c34f68..4bea8f4bc 100644 --- a/README.korean.md +++ b/README.korean.md @@ -26,15 +26,15 @@
-###### [운영 위원회](#운영-위원회)와 [협력자분들](#협력자)이 구축하고 유지하고 있습니다 +###### [운영 위원회](#운영-위원회)와 [협력자분들](#공동-저자)에 의해 구축되고 유지되고 있습니다 # 최근 모범사례와 뉴스 -- **:tada: Node.js 모범사례가 4만 별에 도달했습니다**: Thank you to each and every contributor who helped turning this project into what it is today! We've got lots of plans for the time ahead, as we expand our ever-growing list of Node.js best practices even further. +- **:tada: Node.js 모범사례가 4만 별에 도달했습니다**: 이 프로젝트가 지금 여기까지 올 수 있도록 도와주신 모든 협력자분들께 감사드립니다. 앞으로도 계속 늘어나는 Node.js 모범사례들을 추가하여 더욱더 확장시킬 계획입니다. -- **:rocket: 새로운 모범사례 둘 추가**: We've been working hard on two new best practices, a section about [using npm ci](https://github.com/goldbergyoni/nodebestpractices#-519-install-your-packages-with-npm-ci) to preview the dependency state in production environments and another on [testing your middlewares in isolation](https://github.com/goldbergyoni/nodebestpractices#-413-test-your-middlewares-in-isolation) +- **:rocket: 새로운 모범사례 둘 추가**: 그동안 열심히 만든 dependency state를 production 환경에서 미리 볼 수 있는 [npm ci 사용](https://github.com/goldbergyoni/nodebestpractices#-519-install-your-packages-with-npm-ci) 관련 부분과, [미들웨어 격리 테스트](https://github.com/goldbergyoni/nodebestpractices#-413-test-your-middlewares-in-isolation)에 관한 새로운 모범사례들을 두 개 추가했습니다. -- **:whale: Node.js + Docker 모범사례**: We've opened a [call for ideas](https://github.com/goldbergyoni/nodebestpractices/issues/620) to collect best practices related to running dockerized Node.js applications. If you've got any further best practices, don't hesitate to join the conversation! +- **:whale: Node.js + Docker 모범사례**: dockerized 된 Node.js 애플리케이션 관련 모범사례 [아이디어 수집](https://github.com/goldbergyoni/nodebestpractices/issues/620)을 개시합니다. 그 외에 다른 모범사례가 있다면 주저말고 대화에 참여해주세요!

@@ -42,7 +42,7 @@ **1. 이 문서를 읽는 것은 베스트 Node.js 문서 수십개를 읽는 것과 같습니다. -** 이 문서는 Node.js 의 가장 일반적인 Best Practice 모범사례들을 모은 요약집 및 큐레이션입니다. -**2. 가장 큰 모음집이며, 매주 성장하고 있습니다. -** 현재 80개 이상의 모범사례들과 스타일 가이드 및 아키텍처 관련 팁들을 제공하고 있습니다. 이 문서를 계속 갱신하는 새로운 이슈들과 PR들이 매일 나오고 있습니다. 이 문서의 잘못된 코드를 고치거나 새로운 아이디어들을 제안하는 것은 매우 환영합니다. [마일스톤 보러가기](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) +**2. 가장 큰 모음집이며, 매주 성장하고 있습니다. -** 현재 80개 이상의 모범사례들과 스타일 가이드 및 아키텍처 관련 팁들을 제공하고 있습니다. 이 문서를 계속 갱신하는 새로운 이슈들과 PR들이 매일 나오고 있습니다. 이 문서의 잘못된 코드를 고치거나 새로운 아이디어들을 제안하는 것은 매우 환영합니다. [글쓰기 지침은 여기](/.operations/writing-guidelines.md)서 확인하세요 **3. 항목 대부분은 추가적인 정보가 있습니다 -** 항목 옆쪽에 존재하는 **🔗자세히 보기** 링크에서 코드 예제, 참조 블로그 또는 기타 정보들을 확인 할 수 있습니다. @@ -64,15 +64,15 @@ ## ![✔] 1.1 컴포넌트 기반으로 설계하라 -**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 최악의 함정은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 단일로 통째로 짜여진 monolith 코드는 개발자가 새로운 기능들을 추가하는 속도를 느려지게 한다. 그 대신에 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 안에서 각 단위가 작고 간단하게 유지되도록 해라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인해라. +**핵심요약:** 큰 프로젝트에서 빠지기 쉬운 최악의 함정은 수백개의 의존성을 가진 커다란 소스코드를 유지보수하는 것이다. 그렇게 단일로 통째로 짜여진 monolith 코드는 개발자가 새로운 기능을 추가하는 속도가 느려지게 한다. 그 대신에 코드를 컴포넌트로 나누고, 각각의 컴포넌트가 자신의 폴더 혹은 할당된 코드베이스를 안에서 작고 간단한 단위로 유지되도록 해라. 아래의 '자세히 보기'를 눌러 올바른 프로젝트 구조의 예시를 확인해라. -**그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 어떻게 영향을 미치는지 알기가 힘들어 의존하고 있는 다른 컴포넌트를 망칠까봐 두려워 하면 배포는 느려지고 더 위험해진다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않다. +**그렇게 하지 않을 경우:** 새로운 기능을 작성하는 개발자가 변경사항이 어떤 영향을 미치는지 알기가 힘들면 의존하고 있는 다른 컴포넌트를 망칠까 두려워 하게 되고, 이는 배포를 더 느리고 더 불안전하게 만든다. 비지니스 단위가 나눠져 있지 않으면 확장(scale-out)하기도 쉽지 않게 된다. 🔗 [**자세히 보기: 컴포넌트로 구조화하기**](/sections/projectstructre/breakintcomponents.korean.md)

-## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라. +## ![✔] 1.2 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라 **핵심요약:** 각각의 컴포넌트는 웹, 로직, 데이터 접근 코드을 위한 객체인 '계층'을 포함해야 한다. 이것은 우려할 만한 요소들을 깨끗하게 분리할 뿐만 아니라 모의 객체(mock)를 만들어 테스트하기 굉장히 쉽게 만든다. 이것이 굉장히 일반적인 패턴임에도, API 개발자들은 웹 계층의 객체 (Express req, res)를 비지니스 로직과 데이터 계층으로 보내서 계층을 뒤섞어버리는 경향이 있다. 그렇게 하는것은 당신의 어플리케이션에 의존성을 만들고 Express에서만 접근 가능하도록 만든다. @@ -86,7 +86,7 @@ **핵심요약:** 커다란 코드 기반으로 구성되어있는 커다란 앱에서는 로깅, 암호화 같은 횡단 관심사(cross-cutting-concern)가 있는 유틸의 경우 당신이 쓴 코드로 감싸진 private NPM package의 형태로 노출이 되어야 한다. 이것은 여러 코드 기반과 프로젝트들에게 그것들을 공유할 수 있도록 해준다. -**그렇게 하지 않을 경우:** 당신 자신만의 배포 및 의존성 바퀴(wheel)를 새로 발명해야 할 것이다. +**그렇게 하지 않을 경우:** 당신 자신만의 배포 및 종속 바퀴(dependency wheel)를 새로이 발명해야 할 것이다. 🔗 [**자세히 보기: 기능으로 구조화 하기**](/sections/projectstructre/wraputilities.korean.md) @@ -94,9 +94,9 @@ ## ![✔] 1.4 Express의 app과 server를 분리하라 -**핵심요약:** [Express](https://expressjs.com/)앱을 통째로 하나의 큰 파일에 정의하는 나쁜 습관은 피해라 - 'Express' 정의를 최소한 둘로는 나누자: API 선언(app.js)과 네트워크 부분(WWW). 더 좋은 구조는 API 선언을 컴포넌트 안에 놓는 것이다. +**핵심요약:** [Express](https://expressjs.com/) 앱을 통째로 하나의 큰 파일에 정의하는 나쁜 습관은 피해라 - 'Express' 정의를 최소한 둘로는 나누자: API 선언(app.js)과 네트워크 부분(WWW)으로. 더 좋은 구조는 API 선언을 컴포넌트 안에 놓는 것이다. -**그렇게 하지 않을 경우:** HTTP 요청으로만 API 테스트가 가능해진다 (커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것이 크게 즐겁지는 않을 것이다. +**그렇게 하지 않을 경우:** HTTP 요청으로만 API 테스트가 가능하게 된다 (커버리지 보고서를 생성하기가 더 느려지고 훨씬 힘들어진다). 수백줄의 코드를 하나의 파일에서 관리하는 것이 크게 즐겁지는 않을 것이다. 🔗 [**자세히 보기: Express를 'app'과 'server'로 분리하기**](/sections/projectstructre/separateexpress.korean.md) @@ -104,9 +104,9 @@ ## ![✔] 1.5 환경을 인식하는, 보안적인, 계층적인 설정을 사용하라 -**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경 변수 모두에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 밖에서 관리되어야하고 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. +**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경변수 모두에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 밖에서 관리되어야하며 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. -**그렇게 하지 않을 경우:** 위의 구성 요구사항 중 하나라도 만족시키지 못한다면 개발팀 혹은 데브옵스팀을 늪으로 몰아갈 수 있다. 십중팔구 둘 다. +**그렇게 하지 않을 경우:** 위의 구성 요구사항 중 하나라도 만족시키지 못한다면 개발팀이나 데브옵스팀을 늪으로 몰아갈 수 있다. 십중팔구 둘 다. 🔗 [**자세히 보기: 구성 모범 사례**](/sections/projectstructre/configguide.korean.md) @@ -120,7 +120,7 @@ **핵심요약:** 비동기 에러를 콜백 스타일로 처리하는 것은 지옥으로 가는 급행열차나 마찬가지 (혹은 파멸의 피라미드). 당신이 코드에 줄 수 있는 가장 큰 선물은 평판이 좋은 promise 라이브러리를 사용하거나 훨신 작고 친숙한 코드 문법인 try-catch를 사용하게 해주는 async-await를 사용하는 것이다. -**그렇게 하지 않을 경우:** Node.js 콜백 스타일인 function(err, response)는 에러 처리와 일반 코드의 혼합, 코드의 과도한 중첩, 이상한 코딩 패턴 때문에 유지보수가 불가능한 코드로가는 확실한 길이다. +**그렇게 하지 않을 경우:** Node.js의 function(err, response) 콜백 스타일은 에러 처리와 일반 코드의 혼합, 코드의 과도한 중첩, 어색한 코딩 패턴 때문에 유지보수가 불가능한 코드로 가는 확실한 길이다. 🔗 [**자세히 보기: 콜백 피하기**](/sections/errorhandling/asyncerrorhandling.korean.md) @@ -128,9 +128,9 @@ ## ![✔] 2.2 내장된 Error 객체만 사용하라 -**핵심요약:** 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던진다(throw). 이것은 에러처리 로직과 모듈 사이의 상호운영성을 복잡하게 한다. 당신이 promise를 거부(reject)하든, 예외를 던지든, 에러를 냈건 내장된 Error 객체를 이용하는 것은 균일성을 향상하고 정보의 손실을 방지하게 만들것이다. +**핵심요약:** 많은 사람들이 문자열이나 사용자가 임의로 정의한 타입으로 에러를 던지곤 하는데(throw), 이것은 에러처리 로직과 모듈 사이의 상호운영성을 복잡하게 한다. 당신이 promise를 거부(reject)하든, 예외를 던지든, 에러를 내던간에, 내장된 Error 객체만 이용하는 것이 균일성을 향상하고 정보의 손실을 방지해준다. -**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워 질것이다. 게다가 임의적인 타입으로 에러를 나타내는 것은 스택 정보(stack trace)와 같은 중요한 에러 관련 정보 손실을 일으킬 수 있다! +**그렇게 하지 않을 경우:** 일부 컴포넌트를 호출할때 어떤 에러의 타입이 반환될지 불확실해져서 적절한 에러처리가 매우 어려워진다. 게다가 임의적인 타입으로 에러를 나타내는 것은 스택 정보(stack trace)와 같은 중요한 에러 관련 정보 손실을 일으킬 수 있다! 🔗 [**자세히 보기: 내장된 Error 객체 사용하기**](/sections/errorhandling/useonlythebuiltinerror.korean.md) @@ -146,7 +146,7 @@

-## ![✔] 2.4 에러를 Express 중간에서 처리하지 말고 한군데에서 집중적으로 처리해라 +## ![✔] 2.4 에러를 Express 미들웨어에서 처리하지 말고 한군데에서 집중적으로 처리해라 **핵심요약:** 관리자에게 메일을 보내거나 로깅을 하는 것과 같은 에러 처리는 에러가 발생할 때 모든 엔드포인트 (예를 들어 Express 미들웨어, cron 작업, 단위 테스트 등)가 호출하는 에러전용 중앙집중 객체로 캡슐화 되어야한다. @@ -166,11 +166,11 @@

-## ![✔] 2.6 이상한 것이 들어왔을때 프로세스를 정상적으로 중단하라 +## ![✔] 2.6 낯선이가 들어오면 프로세스를 적절하게 중단하라 **핵심요약:** 알수 없는 에러(프로그래머 에러, 모범사례 2.3번 참조)가 발생하면 어플리케이션의 건강상태가 불확실해진다. 일반적인 방법은 [Forever](https://www.npmjs.com/package/forever)나 [PM2](http://pm2.keymetrics.io/) 같은 '재시작' 도구로 프로세스를 다시 시작하는 것이다. -**그렇게 하지 않을 경우:** 익숙치 않은 예외가 발생하면 일부 객체가 오류 상태 (예를 들어 전역적으로 사용되지만 내부 오류로 인해 이벤트를 더이상 내보내지 않는 event emitter)라서 향후의 모든 요청을 실패시키거나 미친듯이 작동할 수 있다. +**그렇게 하지 않을 경우:** 익숙치 않은 예외가 발생하면 일부 객체가 오류 상태 (예를 들어 전역적으로 사용되지만 내부 오류로 인해 이벤트를 더이상 내보내지 않는 event emitter)라서 향후의 모든 요청을 실패시키거나 미친것처럼 작동할 수 있다. 🔗 [**자세히 보기: 프로세스 중단하기**](/sections/errorhandling/shuttingtheprocess.korean.md) @@ -178,7 +178,7 @@ ## ![✔] 2.7 에러 확인을 용이하게 해주는 안정된 로거를 사용하라 -**핵심요약:** Winston, Bunyan 혹은 Log4J와 같이 자리를 잡은 로깅 도구들은 에러를 발견하고 이해하는 속도를 높여준다. 그러니 console.log은 잊어버려라. +**핵심요약:** Winston, Bunyan 혹은 Log4J와 같이 자리를 잡은 로깅 도구들은 에러를 발견하고 이해하는 속도를 높여준다. 그러니 console.log은 쓰지 마라 **그렇게 하지 않을 경우:** 로그 검색 도구나 제대로 된 로그 뷰어 없이 console.log을 훑어보거나 복잡하게 꼬인 텍스트 파일을 일일이 읽어 보는 것은 야근을 부를 수 있다. @@ -188,9 +188,9 @@ ## ![✔] 2.8 당신이 선호하는 테스트 프레임워크로 에러 흐름을 테스트하라 -**핵심요약:** 전문 자동화 QA든 일반 수동 개발자 테스트든 당신의 코드가 긍정적인 상황에서 잘 동작할 뿐만 아니라 올바른 에러를 처리하고 반환하는지 확실히 하라. Mocha & Chai와 같은 테스트 프레임워크는 이것을 쉽게 처리 할수 있다("Gist popup"안의 코드 예제를 확인하라). +**핵심요약:** 전문적인 자동화 QA든 일반적인 수동 개발자 테스트든 당신의 코드가 긍정적인 상황에서 잘 동작할 뿐만 아니라 올바른 에러를 처리하고 반환하는지도 확실히 하라. Mocha & Chai와 같은 테스트 프레임워크를 쓰면 쉽게 처리할 수 있다 ("Gist popup"안의 코드 예제를 확인하라). -**그렇게 하지 않을 경우:** 자동이든 수동이든 테스트가 없다면 당신은 당신의 코드가 올바른 에러를 반환하는지 믿지 못할 것이다. 의미가 있는 에러가 없다면 에러 처리는 없는 것이다. +**그렇게 하지 않을 경우:** 자동이든 수동이든 테스트가 없다면 당신은 당신의 코드가 올바른 에러를 반환하는지 믿지 못할 것이다. 의미 있는 에러가 없다면 에러 처리도 없는 것이다. 🔗 [**자세히 보기: 에러 흐름 테스트하기**](/sections/errorhandling/testingerrorflows.korean.md) @@ -198,7 +198,7 @@ ## ![✔] 2.9 APM 제품을 사용하여 에러와 다운타임을 확인하라 -**핵심요약:** APM이라고 불리는 모니터링 및 성능 제품은 미리 알아서 코드베이스와 API를 측정하고 자동적으로 당신이 놓친 에러, 충돌, 느린부분을 강조 표시해준다. +**핵심요약:** 모니터링 및 성능 제품(APM)은 미리 알아서 코드베이스와 API를 측정하고 자동적으로 당신이 놓친 에러, 충돌, 느린 부분을 강조 표시해준다. **그렇게 하지 않을 경우:** API의 성능과 다운타임을 측정하기위해 많은 노력을 들여야 할지도 모른다. 아마 당신은 실제 상황에서 어떤 코드 부분이 가장 느린지, 그것이 UX에 어떻게 영향을 미칠지 절대 알수없을 것이다. @@ -208,7 +208,7 @@ ## ![✔] 2.10 처리되지 않은 promise 거부(unhandled promise rejection)를 잡아라 -**핵심요약:** promise안에서 발생한 예외는 개발자가 명시적으로 처리하는 것을 잊게되면 삼켜지고 버려지게 된다. 당신의 코드가 process.uncaughtException 이벤트를 구독하고 있다고해도 말이다! 이것을 극복하기위해 process.unhandledRejection 이벤트를 등록하라. +**핵심요약:** promise 안에서 발생한 예외는 개발자가 명시적으로 처리하지 않는 한 삼켜져 버려지게 된다. 당신의 코드가 `process.uncaughtException` 이벤트를 구독하고 있다고해도 마찬가지다! `process.unhandledRejection` 이벤트를 등록해서 이것을 극복해라. **그렇게 하지 않을 경우:** 당신의 에러는 삼켜지고 어떤 흔적도 남기지 않을 것이다. 걱정할 것이 없긴 하다. @@ -218,7 +218,7 @@ ## ![✔] 2.11 전용 라이브러리를 이용해 인자값이 유효한지 검사하여 빠르게 실패하라(fail fast) -**핵심요약:** 나중에 처리하기가 더 힘들어지는 지저분한 버그를 피하기 위해 Assert API입력은 당신의 Express 모범사례가 되어야 한다. 당신이 Joi와 같은 유용한 헬퍼 라이브러리를 사용하지 않는 이상 유효성 검사 코드는 일반적으로 지루하다. +**핵심요약:** 나중에 처리하기가 더 힘들어지는 지저분한 버그를 피하기 위해 Assert API 입력은 당신의 Express 모범사례가 되어야 한다. 당신이 Joi와 같은 유용한 헬퍼 라이브러리를 사용하지 않는 이상 유효성 검사 코드는 일반적으로 손이 많이 간다. **그렇게 하지 않을 경우:** 이런 상황을 생각해보자. 당신의 함수가 "Discount"라는 숫자를 받아야하는데 요청하는 사람이 넘겨주는 것을 깜빡했다. 그 후에 당신의 코드는 Discount!=0인지 아닌지 체크한다(사실 허용된 Discount의 값은 0보다 커야 한다). 그러면 사용자가 할인을 받게될 것이다. 보이는가? 엄청나게 지저분한 버그이다. @@ -234,7 +234,7 @@ **핵심요약:** [ESLint](https://eslint.org)는 발생 가능한 코드 에러를 체크하고 껄끄러운 간격(spacing)문제를 식별하는 것부터 프로그래머가 분별없이 에러를 던지는 것과 같은 코드의 심각한 안티 패턴을 감지하여 코드 스타일을 바꾸는 것에 대한 사실상의 표준이다. ESLint도 자동으로 코드스타일을 고칠 수 있지만 [prettier](https://www.npmjs.com/package/prettier)와 [beautify](https://www.npmjs.com/package/js-beautify)같은 수정 부분의 포맷을 맞춰주는 강력한 툴이 있고 ESLint와 함께 작동된다. -**그렇게 하지 않을 경우:** 프로그래머가 쓸데없는 간격과 한줄의 길이(line-width) 문제에 대해서 집중해야하고 프로젝트의 코드스타일에 대해 과도하게 생각하느라 시간을 낭비해야할 수도 있다. +**그렇게 하지 않을 경우:** 프로그래머가 쓸데없이 간격과 각 줄의 길이(line-width) 문제에 집중하고 프로젝트의 코드스타일에 대해 과도하게 생각하느라 시간을 낭비하게 된다.

@@ -242,7 +242,7 @@ **핵심요약:** vanlla JS만 지원하는 ESLinst의 표준 규칙 위에 [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha), [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security)와 같은 Node에 특화된 플러그인을 같이 사용하라. -**그렇게 하지 않을 경우:** 많은 결함이 있는 Node.js 코드 패턴들이 레이더에서 벗어날 수 있다. 예를 들어 프로그래머는 변수로된 파일경로를 이용해 `require(파일경로변수)`로 파일을 가져올수 있다. 이것은 공격자들이 어떤 JS script도 실행시킬 수 있게 한다. Node.js linter는 그러한 패턴을 감지하고 미리 알려준다. +**그렇게 하지 않을 경우:** 많은 결함이 있는 Node.js 코드 패턴들이 안중에서 벗어날 수 있다. 예를 들어 프로그래머는 변수로된 파일경로를 이용해 require(파일경로변수)로 파일을 가져올수 있다. 이것은 공격자들이 어떤 JS script도 실행시킬 수 있게 한다. Node.js linter는 그러한 패턴을 감지하고 미리 알려준다.

@@ -253,29 +253,65 @@ ### 코드 예제 ```javascript -// Do +// 좋은 예 function someFunction() { - // code block + // 코드 블록 } -// Avoid +// 나쁜 예 function someFunction() { - // code block + // 코드 블록 } ``` -**그렇게 하지 않을 경우:** 이 모범사례를 적용하지 않는 것은 아래의 Stackoverflow 스레드에서 보는 바와 같이 예기치못한 결과로 이어질 수 있다. +**그렇게 하지 않을 경우:** 이 모범사례를 적용하지 않는 것은 아래의 StackOverflow 스레드에서 보는 바와 같이 예기치못한 결과로 이어질 수 있다. 🔗 [**자세히 보기:** "왜 결과가 중괄호의 위치에 따라 달라지는 거죠?" (Stackoverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

-## ![✔] 3.4 세미콜론을 잊지 마라 +## ![✔] 3.4 문장을 제대로 분리해라 -**핵심요약:** 만장일치로 동의하지는 않겠지만 각 문장의 끝에 세미콜론을 붙이는 것은 여전히 권장사항이다. 이것은 당신의 코드를 읽는 다른 프로그래머가 좀더 잘 읽게하고 명확하게 할것이다. +당신이 문장 끝에 세미콜론을 붙이든 아니든, 제대로 문장을 끝내지 않거나 자동 세미콜론 삽입과 관련된 흔한 실수들을 알아두면 잦은 구문 오류 (syntax error)를 제거하는데 도움이 된다. -**그렇게 하지 않을 경우:** 이전 섹션에서 본것처럼 자바스크립트의 인터프리터는 세미콜론이 없으면 의도되지 않은 결과를 야기할수 있기에 자동으로 문장의 끝에 세미콜론을 붙인다. +**핵심요약:** ESLint를 써서 제대로 문장을 끝내고 있는지 경각심을 불러일으켜라. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/)는 자동으로 이 문제를 해결해준다. + +**그렇게 하지 않을 경우:** 이전 섹션에서 본것처럼 자바스크립트의 인터프리터는 세미콜론이 없으면 자동으로 문장의 끝에 세미콜론을 붙이거나, 문장이 끝났어야 함에도 끝났다고 인지하지 못하여 의도되지 않은 결과를 야기할 수 있다. 대부분의 의도하지 않은 에러들은 assignment를 사용하고 imediate invoked function expression을 사용하는 것을 피함으로써 예방할 수 있다. + +### 코드 예제 + +```javascript +// 좋은 예 +function doThing() { + // ... +} + +doThing() + +// 좋은 예 + +const items = [1, 2, 3] +items.forEach(console.log) + +// 나쁜 예 — 예외 발생 +const m = new Map() +const a = [1,2,3] +[...m.values()].forEach(console.log) +> [...m.values()].forEach(console.log) +> ^^^ +> SyntaxError: Unexpected token ... + +// 나쁜 예 — 예외 발생 +const count = 2 // 2()를 호출하려 하지만, 2는 함수가 아니다 +(function doSomething() { + // 이쁜짓 +}()) +// immediate invoked function 전에 세미콜론을 놓던나, const 정의 후에 놓거나, 익명함수의 반환값을 변수에 저장하던가, 아니면 아예 IIFE를 쓰지 마라 +``` + +🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) +🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline)

@@ -283,8 +319,7 @@ function someFunction() **핵심요약:** 클로저와 콜백을 포함한 모든 함수에 이름을 붙여라. 익명함수를 피해라. 이것은 노드 앱을 프로파일링 할때 특히 유용하다. 모든 함수를 명명하는 것은 당신이 메모리 스냅샷을 확인할때 당신이 보고있는 것이 무엇인지 쉽게 이해 할수있도록 해준다. -**그렇게 하지 않을 경우:** -당신이 익명함수에서 메모리 소비가 많다는 것을 확인 했을 때 코어 덤프(메모리 스냅샷)을 이용해 프로덕션 문제를 디버깅하는 것이 어려울 수도 있습니다. +**그렇게 하지 않을 경우:** 당신이 익명함수에서 메모리 소비가 많다는 것을 확인 했을 때 코어 덤프(메모리 스냅샷)을 이용해 프로덕션 문제를 디버깅하는 것이 어려울 수도 있습니다.

@@ -312,9 +347,9 @@ function doSomething() {}

-## ![✔] 3.7 let보다는 const를 사용하라. var는 갖다버려라 +## ![✔] 3.7 let보다는 const를 사용하라. var는 개나 줘라 -**핵심요약:** `const`를 사용한다는 것은 변수에 한번 값이 할당되면 다시 할당할 수 없다는 것을 의미한다. `const`를 선호하는 것은 같은 변수를 다른 용도로 사용하는 것을 방지하고 당신의 코드를 더 깔끔하게 만드는데 도움을 준다. for루프처럼 변수가 재할당 되어야 할 필요가 있으면 `let`을 사용하여 선언하라. `let`의 또 다른 중요한 부분은 선언된 변수를 사용하는 것이 변수가 정의된 블록범위(block scope) 안에서만 가능하다는 것이다. `var`는 블록범위가 아니라 함수범위(function scope)이며 이제 대신할 수 있는 const와 let이 있으므로 [ES6에서는 사용하면 안된다](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70). +**핵심요약:** `const`를 사용한다는 것은 변수에 한번 값이 할당되면 다시 할당할 수 없다는 것을 의미한다. `const`를 선호하는 것은 같은 변수를 다른 용도로 사용하는 것을 방지하고 당신의 코드를 더 깔끔하게 만드는데 도움을 준다. for 루프처럼 변수가 재할당 되어야 할 필요가 있으면 `let`을 사용하여 선언하라. `let`의 또 다른 중요한 부분은 선언된 변수를 사용하는 것이 변수가 정의된 블록범위(block scope) 안에서만 가능하다는 것이다. `var`는 블록범위가 아니라 함수범위(function scope)이며 이제 대신할 수 있는 const와 let이 있으므로 [ES6에서는 사용하면 안된다](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70). **그렇게 하지 않을 경우:** 자주 변경되는 변수를 따라가려면 디버깅이 훨씬 더 번거로워 진다. @@ -330,20 +365,20 @@ function doSomething() {}

-## ![✔] 3.9 require는 파일에 직접하지말고 폴더에 하라 +## ![✔] 3.9 파일 대신 폴더를 require 해라 -**핵심요약:** 폴더에서 모듈과 라이브러리를 개발할 때 모든 소비자가 그것을 거치도록 모듈의 내부를 노출하는 index.js 파일을 둬라. 이것은 모듈의 '인터페이스'역할을 하며 계약을 위반하지 않으면서 미래의 변경사항에 대해 유연하게 대처하도록 해준다. +**핵심요약:** 폴더 안에서 모듈/라이브러리를 개발할 때, 모든 소비자가 내부를 노출하는 index.js 파일을 거치도록 해라. 이것은 모듈의 '인터페이스'역할을 하며 계약을 위반하지 않으면서 미래의 변경사항에 대해 유연하게 대처하도록 해준다. **그렇게 하지 않을 경우:** 파일 내부의 구조 혹은 서명을 변경하면 클라이언트와의 인터페이스가 손상될 수 있다. ### 코드 예제 ```javascript -// 이렇게 하라 +// 좋은 예 module.exports.SMSProvider = require('./SMSProvider'); module.exports.SMSNumberResolver = require('./SMSNumberResolver'); -// 피하라 +// 나쁜 예 module.exports.SMSProvider = require('./SMSProvider/SMSProvider.js'); module.exports.SMSNumberResolver = require('./SMSNumberResolver/SMSNumberResolver.js'); ``` @@ -389,9 +424,9 @@ null == undefined // true ## ![✔] 3.12 두꺼운(=>) 화살표 함수를 사용하라 -**핵심요약:** async-await을 사용하고 함수 인자를 사용하는 것을 피하는 것이 권장되지만 promise와 콜백을 받는 예전 API를 다룰 때는 화살표 함수가 코드 구조를 더 작게해주고 루트 함수의 어휘적 맥락(lexical context)을 유지시켜 준다. (예를 들어 'this') +**핵심요약:** async-await을 사용하고 함수 인자를 사용하는 것을 피하는 것이 권장되지만 promise와 콜백을 받는 예전 API를 다룰 때는 화살표 함수가 코드 구조를 더 작게해주고 루트 함수의 어휘적 맥락(lexical context)을 유지시켜 준다. (`this`라던가) -**그렇게 하지 않을 경우:** 더 긴 코드(ES5의 function)은 버그에 더 취약하고 읽기가 번거롭다. +**그렇게 하지 않을 경우:** (ES5의 함수 내의) 긴 코드는 버그에 취약하고 읽기도 번거롭다. 🔗 [**Read mode: 화살표 함수를 받아들일 시간이다**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) @@ -403,7 +438,7 @@ null == undefined // true ## ![✔] 4.1 다른건 못해도 최소한 API (컴포넌트) 테스트는 써라 -**핵심요약:** 대부분의 프로젝트는 짧은 일정으로 인해 자동화 된 테스트가 없거나 후일 통제를 벗어난 이른바 "시험용 프로젝트" 라는 이유로 버려진다. 그러므로, 쓰기에도 제일 쉽고 유닛 테스트보다 더 넓게 커버할 수 있는 API 테스트를 우선으로 시작해라. ([포스트맨](https://www.getpostman.com/) 같은 도구를 이용하지 않고도 API 테스트를 쓸 수 있다.) 나중에 시간과 자원이 더 나면 그때 유닛테스트, DB 테스트, 성능 테스트 등의 상급 테스트 종류를 계속 더해라. +**핵심요약:** 대부분의 프로젝트는 짧은 일정으로 인해 자동화 된 테스트가 없거나 후일 통제를 벗어난 이른바 "시험용 프로젝트" 라는 이유로 버려진다. 그러므로, 쓰기에도 제일 쉽고 유닛 테스트보다 더 넓게 커버할 수 있는 API 테스트를 우선으로 시작해라. ([포스트맨](https://www.getpostman.com/) 같은 도구를 이용하지 않고도 API 테스트를 쓸 수 있다.) 나중에 시간과 자원이 더 나면 그때 유닛테스트, DB 테스트, 성능 테스트 등의 좀 더 발전된 테스트 종류를 계속 더해라. **그렇게 하지 않을 경우:** 유닛 테스트 쓰는데 시간을 몇 날 며칠을 쓰고선 시스템 커버리지가 20% 밖에 안된다는걸 깨닫게 될 수 있다. @@ -424,7 +459,6 @@ null == undefined // true **핵심요약:** 테스트를 다음 세가지 부분으로 명확히 나누어라: 준비 (arrange), 실행 (act), 표명 (assert). 제일 먼저 테스트를 준비하고, 그 다음 테스트 단위(unit under test)를 실행하고, 마지막은 확인 단계다. 이 구조를 따르는 것은 읽는이가 힘들여 머리를 쓰지 않고도 테스트 설계를 이해할 수 있도록 보장한다. - **그렇게 하지 않을 경우:** 매일 하루종일 주요 코드를 읽는데 시간을 오래 쓰는 것도 모자라 간단해야하는 테스트 부분에도 열심히 머리를 써야 할 것이다. @@ -434,7 +468,7 @@ null == undefined // true ## ![✔] 4.4 린터를 사용해서 코드의 문제점을 찾아내라 -**핵심요약:** 코드 린터를 사용해서 기본적인 품질을 확인하고 안티패턴을 초기에 찾아내라. 테스트 하기도 전에 먼저 돌리고, pre-commit git-hook으로 추가해서 문제를 검토하고 정정하는 시간을 최소화해라. [3문단](#3-코드-스타일)의 코드 스타일 관례들도 확인해라. +**핵심요약:** 코드 린터를 사용해서 기본적인 품질을 확인하고 안티패턴을 초기에 찾아내라. 테스트 하기도 전에 먼저 돌리고, pre-commit git-hook으로 추가해서 문제를 검토하고 정정하는 시간을 최소화해라. [3문단](#3-코드-스타일)의 코드 스타일 관례들도 확인해라. **그렇게 하지 않을 경우:** 안티패턴과 취약한 코드가 상용환경에 넘어갈 수도 있다. @@ -444,7 +478,6 @@ null == undefined // true **핵심요약:** 테스트간의 간섭과 결합도(coupling)를 최소화하고 테스트 흐름을 추론하기 쉽도록 테스트들은 각자 자신만의 DB 행 (row) 집합을 만들어 써야 한다. 테스트가 DB 데이터를 가져오거나 데이터가 존재한다고 간주해야 할 때마다 다른 레코드를 변형시키지 않도록 그 데이터를 직접 추가하여야 한다. - **그렇게 하지 않을 경우:** 실패하는 테스트때문에 전개(deployment)가 중단되었다고 가정해 보자. 이제 팀원들은 소중한 시간을 조사하는데 소모한 뒤 슬픈 결론을 내릴 것이다: 시스템은 잘 작동하지만, 테스트간의 상호 간섭으로 빌드 실패. 🔗 [**자세히 보기: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md) @@ -455,15 +488,15 @@ null == undefined // true **핵심요약:** Express같은 네임드 dependency도 알려진 취약점이 있다. 이건CI에서 매 빌드마다 호출할 수 있는 🔗 [npm audit](https://docs.npmjs.com/cli/audit)나 🔗 [snyk.io](https://snyk.io)같은 커뮤니티 혹은 상업용 도구를 사용하면 쉽게 해결할 수 있다. -**그렇게 하지 않을 경우:** 전용 도구없이 코드를 취약점 없이 깨끗하게 유지하려면 온라인 출판물들을 항상 유심히 찾아읽어야 한다. 꽤나 성가신 일이다. +**그렇게 하지 않을 경우:** 전용 도구없이 코드를 취약점 없이 깨끗하게 유지하려면 온라인 출판물들을 항상 유심히 찾아읽어야 한다. 한마디로 노가다.

## ![✔] 4.7 테스트를 태그하라 (#테스트) -**핵심요약:** 다른 종류의 테스트는 서로 다른 시나리오를 바탕으로 실행해야한다: 빌드 성공유무 테스트(quick smoke)나 IO 입출력이 없는 테스트는 개발자가 파일을 저장하거나 commit 할 때마다 실행하고, 더 포괄적인 단대단 (end-to-end 테스트는 보통 풀리퀘스트를 제출 할 때마다 실행한다. 이건 테스트를 #cold #api #sanity 와 같은 키워드로 태그해서 원하는 테스트들만 부분적으로 grep 으로 test harness를 검색해서 실행하면 된다. 예를 들면, [모카](https://mochajs.org/)에서 sanity 테스트 그룹만 실행하고 싶다면 이렇게 하면 된다: mocha --grep 'sanity' +**핵심요약:** 다른 종류의 테스트는 서로 다른 시나리오를 바탕으로 실행해야한다: 빌드 성공유무 테스트(quick smoke)나 IO 입출력이 없는 테스트는 개발자가 파일을 저장하거나 commit 할 때마다 실행하고, 더 포괄적인 단대단 (end-to-end) 테스트는 보통 풀리퀘스트를 제출 할 때마다 실행한다. 이건 테스트를 #cold #api #sanity 와 같은 키워드로 태그해서 원하는 테스트들만 부분적으로 grep 으로 test harness를 검색해서 실행하면 된다. 예를 들면, [모카](https://mochajs.org/)에서 sanity 테스트 그룹만 실행하고 싶다면 이렇게 하면 된다: mocha --grep 'sanity' -**그렇게 하지 않을 경우:** 개발자가 조금씩 코드를 바꿀때마다 DB 쿼리를 한다스씩 보내는 테스까지 포함해서 전부 실행하면 개발 속도도 느려지고 개발자들도 점점 테스트를 실행하는걸 꺼리게 된다 +**그렇게 하지 않을 경우:** 개발자가 조금씩 코드를 바꿀때마다 DB 쿼리를 한다스씩 보내는 테스트까지 포함해서 전부 실행하면 개발 속도도 느려지고 개발자들도 점점 테스트를 실행하는걸 꺼리게 된다

@@ -471,13 +504,13 @@ null == undefined // true **핵심요약:** [이스탄불](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc)같은 코드 커버리지 도구가 좋은 이유는 세가지가 있다: 무료이고 (거져먹는거다), 테스트 범위가 줄어드는것을 잡아내주고, 마지막으로 테스트 부조화를 하이라이트한다: 색으로 나타낸 코드 커버리지 리포트를 보다보면 catch 절 같이 테스트 하지 않는 부분들을 알아채기 시작할것이다. (테스트는 보통 경로만 테스트하기에 앱이 에러가 날 경우에는 어떻게 반응하는지는...) 커버리지가 일정 기준 이하로 떨어지면 빌드가 자동으로 실패하게 해라. -**그렇게 하지 않을 경우:** 코드의 상당한 범위가 테스트로 커버되지 않더라도 자동적으로 측정하여 알 길이 없다. +**그렇게 하지 않을 경우:** 코드의 상당한 범위가 테스트로 커버되지 않더라도 자동으로 측정되지 않으니 알 길이 없다.

## ![✔] 4.9 오래되어 뒤떨어진 패키지는 없는지 검사해라 -**핵심요약:** 설치된 패키지중 outdated 된 패키지는 없는지 선호하는 도 (예: 'npm outdated'나 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates))를 써서 확인하고, 심할 경우 빌드가 실패하도록 CI 경로에 이 체크를 주입해라. 예를 들면, 설치된 패키지가 패치 commit 5개 이상 뒤쳐졌거나 (예: 로컬은 1.3.1버젼인데 repository 버젼은 1.3.8이라던가) 제작자가 deprecated 되었다고 태그하면 빌드를 죽이고 이 버젼을 배포하지 못하게 막아라. +**핵심요약:** 설치된 패키지중 outdated 된 패키지는 없는지 선호하는 도구 (예: 'npm outdated'나 [npm-check-updates](https://www.npmjs.com/package/npm-check-updates))를 써서 확인하고, 심할 경우 빌드가 실패하도록 CI 경로에 이 체크를 주입해라. 이를테면 설치된 패키지가 패치 commit 5개 이상 뒤쳐졌거나 (예: 로컬은 1.3.1버젼인데 repository 버젼은 1.3.8이라던가) 제작자가 deprecated 되었다고 태그하면 빌드를 죽이고 이 버젼을 배포하지 못하게 막아라. **그렇게 하지 않을 경우:** 제작자가 직접 불안정하다고 태그한 패키지가 프로덕션에서 놀아날 수 있다 @@ -495,7 +528,7 @@ null == undefined // true **핵심요약:** 정적분석도구(static analysis tool)는 코드의 품질을 객관적으로 개선하고 코드 유지를 쉽게 해준다. 코드스멜을 감지하면 CI 빌드가 실패하도록 정적분석도구를 넣어주면 된다. 이게 단순한 린보팅다 나은 주된 이유로는 여러 파일에 걸친 맥락에서 품질을 점검할 수 있다는 점 (예: 중복된 코드 감지), 더 발달된 분석을 할 수 있다는 점 (예: 코드 복잡도), 코드 문제의 전적과 진전을 따라 볼 수 있다는 점이 있다. 쓸만한 도구의 예를 두가지를 들자면 [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) 와 [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate))가 있다. -**그렇게 하지 않을 경우:** 제아무리 반짝이는 새로나온 라이브러리나 최첨단 기능을 써봤자 코드 품질이 불량하면 버그와 성능은 못고친다 +**그렇게 하지 않을 경우:** 아무리 반짝이는 새로나온 라이브러리나 최첨단 기능을 써봤자 코드 품질이 불량하면 버그와 성능은 못고친다 🔗 [**자세히 보기: Refactoring!**](/sections/testingandquality/refactoring.md) @@ -505,15 +538,15 @@ null == undefined // true **핵심요약:** 지속적 통합 플랫폼(CICD)은 품질 관리 도구(예: 테스트, 린트)들을 돌릴 수 있게 플러그인 생태계가 활발해야 한다. 예전에는 대부분의 프로젝트들이 배우기는 어려워도 커뮤니티도 제일 크고 강력한 플랫폼을 가진 [Jenkins](https://jenkins.io/)를 기본으로 썼다. 요즘엔 [CircleCI](https://circleci.com)등의 SaaS 해결책을 쓰는게 훨씬 더 쉬워졌다. 이런 도구들은 인프라 전체를 관리하는 부담 없이도 유연한 CI 경로를 만들 수 있게 해준다. 결국에는 안전성과 빠름의 상호 절충이다 - 조심해서 선택해라. -**그렇게 하지 않을 경우:** 잘 알려지지 않은 중소 솔루션 업체를 쓰다간 흔치 않은 고급 설정을 써야할 때 막혀버릴 수도 있다. 하지만 반대로, Jenkins를 택하면 인프라를 수축하는데 소중한 시간을 다 빼앗길 수도 있다. +**그렇게 하지 않을 경우:** 잘 알려지지 않은 중소 솔루션 업체를 쓰다간 흔치 않은 고급 설정을 커스터마이징 해야할 때 막혀버릴 수도 있다. 하지만 반대로, Jenkins를 택하면 인프라를 수축하는데 소중한 시간을 다 빼앗길 수도 있다. 🔗 [**자세히 보기: Choosing CI platform**](/sections/testingandquality/citools.korean.md) -## ![✔] 4.13 미들웨어들은 따로 테스트해라 +## ![✔] 4.13 미들웨어들은 격리시켜 테스트해라 -**핵심요약:** 여러 요청에 걸친 막대한 로직을 미들웨어가 수용하는 경우, 웹 프레임워크 전체를 깨우지 않고 따로 테스트 할만한 가치가 있다. {req, res, next} 객체들을 스텁(stub)하여 염탐(spy)하면 쉽게 달성할 수 있다. +**핵심요약:** 여러 요청에 걸친 막대한 로직을 미들웨어가 수용하는 경우, 웹 프레임워크 전체를 깨우지 않고 격리 상태로 테스트 할만한 가치가 있다. {req, res, next} 객체들을 스텁(stub)하여 염탐(spy)하면 쉽게 달성할 수 있다. -**그렇게 하지 않을 경우:** Express 미들웨어의 버그 === 거의 모든 요정의 버그 +**그렇게 하지 않을 경우:** Express 미들웨어의 버그 === 거의 모든 요청의 버그 🔗 [**자세히 보기: Test middlewares in isolation**](/sections/testingandquality/test-middlewares.md) @@ -533,11 +566,11 @@ null == undefined // true

-## ![✔] 5.2. 똑똑한 로깅을 통해 투명성을 높여라 +## ![✔] 5.2. 스마트 로깅으로 투명성을 높여라 -**핵심요약:** 로그는 디버그 텍스트가 모여있는 의미없는 창고일 수도 있고 앱의 상태를 대시보드로 볼수 있도록 만들어 줄 수 있는 유용한 데이터일 수도 있다. 첫날부터 로그를 어떻게 수집, 저장 및 분석하는지 계획하여 원하는 정보(예: 오류율, 서비스 및 서버를 통한 전체 트랜잭션 수행)를 실제로 추출 할지 확실히 하라 +**핵심요약:** 로그는 디버그 텍스트가 모여있는 의미없는 창고일 수도 있고, 앱의 상태를 대시보드로 볼 수 있도록 만들어 줄 수 있는 유용한 데이터일 수도 있다. 원하는 정보 (예: 오류율, 트랜잭션이 서비스와 서버를 거치는 경로를 따라가기, 기타등등)를 쉽게 추출 할 수 있도록 로그를 어떻게 수집, 저장 및 분석하는지 첫날부터 미리 계획해라. -**그렇게 하지 않을 경우:** 당신은 결국 추측하기 힘든 블랙박스에 도달하게고, 필요한 정보를 추가하기 위해 모든 로그를 다시 작성하게 될것이다 +**그렇게 하지 않을 경우:** 이해하기 힘든 블랙박스가 되어버려서 필요한 정보를 추가하기 위해 모든 로그를 다시 작성하게 될것이다 🔗 [**자세히 보기: Increase transparency using smart logging**](/sections/production/smartlogging.md) @@ -547,7 +580,7 @@ null == undefined // true **핵심요약:** Node는 gzip이나 SSL Termination과 같이 CPU 집약적인 작업에 약하다. 이런게 필요할 경우 '실제' 미들웨어 서비스인 nginx, HAproxy 혹은 클라우드 서비스를 사용해야한다 -**그렇게 하지 않을 경우:** 불쌍한 싱글 스레드는 어플리케이션의 코어를 처리하는 대신 인프라 작업을 수행하는 것에 더 바쁘게되고 성능은 저하될 것이다 +**그렇게 하지 않을 경우:** 불쌍한 싱글 스레드가 어플리케이션의 코어를 처리하는 대신 인프라 작업을 수행하느라 바빠져서 성능이 저하될 것이다 🔗 [**자세히 보기: 가능한 모든 것들(예: gzip, SSL)을 reverse proxy에 위임하라**](/sections/production/delegatetoproxy.md) @@ -555,19 +588,19 @@ null == undefined // true ## ![✔] 5.4. 의존성을 잠궈라(Lock dependencies) -**핵심요약:** 코드는 모든 환경에서 동일해야하지만 놀랍게도 npm을 사용하면 기본적으로 환경간에 종속성이 달라질 수 있다. 다양한 환경에서 패키지를 설치하면 패키지의 최신 패치 버전을 가져온다. npm config 파일 인 .npmrc를 사용하여이 문제를 극복하라. 각 환경에 각 패키지의 최신 버전이 아닌 정확한 버전을 저장하도록 알려준다. 또는 세밀하게 제어하려면`npm shrinkwrap`을 사용하라. \* 업데이트 : NPM5 현재 기본적으로 종속성이 잠겨 있다. 새로운 패키지 관리자인 Yarn도 기본적으로 잠겨 있다. +**핵심요약:** 코드는 모든 환경에서 동일해야하지만 놀랍게도 npm을 사용하면 기본적으로 환경간에 종속성이 달라질 수 있는데, 다양한 환경에서 패키지를 설치하면 패키지의 최신 패치 버전을 가져오기 때문이다. npm config 파일인 .npmrc를 사용하여이 문제를 극복해라. 각 환경에 각 패키지의 최신 버전이 아닌 정확한 버전을 저장하도록 알려준다. 또는 세밀하게 제어하려면 `npm shrinkwrap`을 사용하라. \* 업데이트 : NPM5부터는 종속성이 기본적으로 잠겨 있다. 새로운 패키지 관리자인 Yarn도 기본적으로 잠겨 있다. -**그렇게 하지 않을 경우:** QA팀은 코드를 철저히 테스트했지만 테스트 환경과는 다르게 작동하는 버전을 승인할 것이다. 심지어 더 나쁜 것은 같은 프로덕션 클러스터의 여러 서버가 서로 다른 코드를 실행할 수도 있다는 것이다. +**그렇게 하지 않을 경우:** QA팀은 코드를 철저히 테스트 했음에도 테스트 환경과는 다르게 작동하는 버전을 승인할 것이다. 심지어 같은 프로덕션 클러스터의 여러 서버가 서로 다른 코드를 실행할 수도 있다. 🔗 [**자세히 보기: Lock dependencies**](/sections/production/lockdependencies.md)

-## ![✔] 5.5. 올바른 도구를 이용해 프로세스가 가동되는 시간을 보호하라 +## ![✔] 5.5. 올바른 도구를 이용해 프로세스 가동시간을 사수해라 -**핵심요약:** 프로세스는 계속 진행되어야하며 실패시 다시 시작해야한다. 간단한 시나리오의 경우 PM2와 같은 프로세스 관리 도구로도 충분하지만 오늘날 '도커가 사용되는' 세계에서는 클러스터 관리 도구도 고려해야한다 +**핵심요약:** 프로세스는 계속 진행되어야 하며 실패시 다시 시작해야한다. 간단한 시나리오의 경우 PM2와 같은 프로세스 관리 도구로도 충분하지만 오늘날 '도커화 된' 세계에서는 클러스터 관리 도구도 고려해야한다 -**그렇게 하지 않을 경우:** 명확한 전략없이 수십 개의 인스턴스와 너무 많은 도구 (클러스터 관리, 도커, PM2)를 실행하면 개발자가 혼란을 겪을 수 있다 +**그렇게 하지 않을 경우:** 명확한 전략없이 수십 개의 인스턴스와 너무 많은 도구 (클러스터 관리, 도커, PM2)를 실행하면 데브옵스가 혼란을 겪을 수 있다 🔗 [**자세히 보기: Guard process uptime using the right tool**](/sections/production/guardprocess.md) @@ -577,7 +610,7 @@ null == undefined // true **핵심요약:** 기본적으로 Node 어플리케이션은 하나의 CPU 코어에서 실행되고 다른 CPU는 동작하지 않는다. 노드 프로세스를 복제하여 모든 CPU를 활용하는 것은 당신의 몫이다. 중소형 어플리케이션의 경우 노드 클러스터 또는 PM2를 사용하면 된다. 더 큰 앱의 경우 Docker 클러스터(예: K8S, ECS) 또는 Linux 초기화 시스템(예: systemd)을 기반으로하는 배포 스크립트를 사용하여 프로세스를 복제하는 것이 좋다 -**그렇게 하지 않을 경우:** 사용 가능한 리소스의 25%, 혹은 훨씬 적게 활용할 것이다. 일반적인 서버는 CPU 코어가 4개 이상인 점을 감안할 때, Node를 아무 생각없이 배포하게 되면 그 중 단 1 개만 사용하게 될것이다. AWS beanstalk와 같은 PaaS 서비스 사용하더라도 말이다 +**그렇게 하지 않을 경우:** 앱이 사용 가능한 리소스 중 25%(!)에도 못 미치게 활용할 것이다. 일반적인 서버는 CPU 코어가 4개 이상임에도 Node를 기본형으로 배포하면 그 중 단 1 개만 사용하게 될것이다 (AWS beanstalk과 같은 PaaS 서비스 사용해도 마찬가지) 🔗 [**자세히 보기: 모든 CPU를 활용하라**](/sections/production/utilizecpu.md) @@ -597,7 +630,7 @@ null == undefined // true **핵심요약:** 어플리케이션 성능 관리 제품(Application Performance Management, APM)은 코드베이스 및 API를 사전에 측정하여 기존 모니터링 시스템을 뛰어 넘어 서비스 및 계층 전반에서 사용자 경험을 측정한다. 예를 들어 일부 APM 제품은 최종 사용자 측면에서 느리게 로드되는 트랜잭션을 강조 표시 하면서 근본 원인을 제시할 수 있다 -**그렇게 하지 않을 경우:** API 성능 및 가동 중지 시간을 측정하는 데 많은 노력을 기울이게 될 수 있다. 실제 상황에서 가장 느린 코드가 무엇인지, 그리고 이것이 UX에 미치는 영향을 알지 못할 것이다 +**그렇게 하지 않을 경우:** API 성능 및 가동 중지 시간을 측정하는 데 많은 노력을 기울임에도 실전에서 가장 느린 코드 부분이 어느것인지, 그리고 이것이 UX에 미치는 영향을 알지 못할 것이다 🔗 [**자세히 보기: Discover errors and downtime using APM products**](/sections/production/apmproducts.md) @@ -605,29 +638,29 @@ null == undefined // true ## ![✔] 5.9. 당신의 코드는 언제든 상용에 배포될수 있다 -**핵심요약:** 첫날부터 배포를 염두에두고 코드를 작성하라. 다소 모호하게 들릴것 같아서 상용 유지 보수와 밀접하게 관련된 몇 가지 개발 팁을 컴파일해 두었다 (아래의 gist를 클릭하라) +**핵심요약:** 첫날부터 끝을 염두에두고 코드를 작성하라. 다소 모호하게 들릴것 같아서 상용 유지 보수와 밀접하게 관련된 몇 가지 개발 팁을 편찬해 두었다 (아래의 gist를 클릭하라) -**그렇게 하지 않을 경우:** 세계 최고의 IT/DevOps 전문가도 잘못 작성된 코드로 이루어진 시스템은 구하지 못한다 +**그렇게 하지 않을 경우:** 세계 최고의 IT/DevOps 전문가도 형편없이 쓰여진 코드로 이루어진 시스템은 구하지 못한다 🔗 [**자세히 보기: Make your code production-ready**](/sections/production/productioncode.md)

-## ![✔] 5.10. 메모리 사용량 측정 및 보호 +## ![✔] 5.10. 메모리 사용량을 측정하고 보호해라 -**핵심요약:** Node.js는 메모리와 관련하여 논란의 여지가 있다. v8 엔진은 메모리 사용량 (1.4GB)에 대한 제한이 있으며 노드의 코드에서 메모리 누수가 발생하는 알려진 방법이 존재하므로 노드의 프로세스 메모리를 관찰하는 것이 필수적이다. 작은 응용 프로그램에서는 Shell 명령을 사용하여 주기적으로 메모리를 측정 할 수 있지만 중대형 어플리케이션에서는 강력한 모니터링 시스템을 통해 메모리를 감시하는 것을 고려하라 +**핵심요약:** Node.js는 메모리와 관련하여 논란의 여지가 있다: v8 엔진은 메모리 사용량 (1.4GB)에 대한 제한이 있으며 노드의 코드에서 메모리 누수가 발생하는 알려진 방법이 존재하므로 노드의 프로세스 메모리를 관찰하는 것이 필수적이다. 작은 응용 프로그램에서는 Shell 명령을 사용하여 주기적으로 메모리를 측정 할 수 있지만 중대형 어플리케이션에서는 강력한 모니터링 시스템을 통해 메모리를 감시하는 것을 고려하라 -**그렇게 하지 않을 경우:** [월마트](https://www.joyent.com/blog/walmart-node-js-memory-leak)에서 일어났던 것처럼 메모리가 하루에 수백 MB씩 누수 될 수 있다 +**그렇게 하지 않을 경우:** [월마트](https://www.joyent.com/blog/walmart-node-js-memory-leak)에서 일어났던 것처럼 메모리가 하루에 수백 메가바이트씩 누수 될 수 있다 🔗 [**자세히 보기: 메모리 사용량 측정 및 보호**](/sections/production/measurememory.md)

-## ![✔] 5.11. Node.js에서 프론트 엔드 파일 가져오기 +## ![✔] 5.11. Node.js에서 프론트 엔드 자산을 빼라 -**핵심요약:** 단일 스레드 모델로 인해 정적 파일을 많이 처리 할 때 Node.js 성능이 실제로 손상되기 때문에 전용 미들웨어(nginx, S3, CDN 등)를 사용하여 프론트 엔드 컨텐츠를 제공하는게 좋다 +**핵심요약:** Node는 단일 스레드 모델을 쓰기 때문에 정적 파일을 많이 처리하게 되면 성능이 많이 저하되기 때문에 전용 미들웨어(nginx, S3, CDN 등)를 사용하여 프론트 엔드 컨텐츠를 제공하는게 좋다 -**그렇게 하지 않을 경우:** 단일 노드 스레드는 동적 컨텐츠를 전달하는 작업에 리소스를 할당하는 대신 수백 개의 html/images/angular/react 파일을 스트리밍 하느라 분주할 것이다 +**그렇게 하지 않을 경우:** 단일 노드 스레드는 수백 개의 html/이미지/angular/react 파일을 스트리밍 하느라 바빠서 정작 천직인 동적 컨텐츠를 전달하는 작업에는 신경쓸 겨를이 없을것이다. 🔗 [**자세히 보기: Get your frontend assets out of Node**](/sections/production/frontendout.md) @@ -643,9 +676,9 @@ null == undefined // true

-## ![✔] 5.13. 취약점을 자동으로 탐지하는 도구 사용 +## ![✔] 5.13. 취약점을 자동으로 탐지하는 도구를 사용해라 -**핵심요약:** Express와 같은 가장 신뢰할만한 모듈조차도 시스템을 위험에 빠뜨릴 수있는 알려진 취약점이 존재한다. 이는 취약성을 지속적으로 확인하고(로컬 또는 GitHub에서) 경고하는 커뮤니티 및 상용 도구를 사용하여 쉽게 길들여질 수 있으며 일부는 즉시 패치 할 수도 있다 +**핵심요약:** Express와 같은 가장 신뢰할만한 모듈조차도 시스템을 위험에 빠뜨릴 수있는 알려진 취약점이 존재한다. 이는 취약성을 지속적으로 (로컬 또는 GitHub에서) 확인하고 경고하는 커뮤니티 및 상용 도구를 사용하면 쉽게 길들일 수 있으며 일부는 즉시 패치 할 수도 있다 **그렇게 하지 않을 경우:** 전용 도구없이 취약점으로부터 코드를 깨끗하게 유지하려면 새로운 취약점에 대한 데이터를 지속적으로 확인해야 할것이다 @@ -655,7 +688,7 @@ null == undefined // true ## ![✔] 5.14. 로깅을 할때 트랜잭션 ID를 할당하라 -**핵심요약:** 하나의 요청 내에서 각 로그에 동일한 식별자(transaction-id: { some value })를 할당하라. 그렇게하면 로그를 분석할때 에러 전과 후에 어떤 일이 생겼는지 쉽게 알수있다. 비동기적인 특성때문에 Node.js에서 구현하기 쉽지는 않다. 아래의 예제를 확인하라 +**핵심요약:** 하나의 요청 내에서 각 로그에 동일한 식별자(transaction-id: {some value})를 할당하라. 그렇게하면 로그를 분석할때 에러 전과 후에 어떤 일이 생겼는지 쉽게 알수있다. 비동기적인 특성때문에 Node.js에서 구현하기 쉽지는 않다. 아래의 예제를 확인하라 **그렇게 하지 않을 경우:** 이전에 어떤일이 일어났는지에 대한 컨텍스트 없이 에러 로그를 확인하는 것은 문제를 해결하는 것을 더 어렵고 느리게 만든다 @@ -675,13 +708,13 @@ null == undefined // true ## ![✔] 5.16. 원자성의 자동화된 무중단 배포를 설계하라 -**핵심요약:** 연구 결과에 따르면 자주 배포를 하는 팀이 상용버전에서 심각한 에러가 발생할 가능성을 낮춘다고 한다. 위험이 따르는 수동적인 과정과 서비스의 중지시간이 필요하지 않은 빠르고 자동화된 배포는 배포 프로세스를 크게 향상시킨다. 간소화된 배포의 표준이 된 Docker와 CI 도구를 결합하여 이를 달성할 수 있다. +**핵심요약:** 연구 결과에 따르면 자주 배포를 하는 팀이 상용버전에서 심각한 에러가 발생할 가능성을 낮춘다고 한다. 위험이 따르는 수동적인 과정과 서비스 다운타임이 필요하지 않은 빠르고 자동화된 배포는 배포 프로세스를 크게 향상시킨다. 간소화된 배포의 표준이 된 Docker와 CI 도구를 결합하여 이를 달성할 수 있다. **그렇게 하지 않을 경우:** 오래걸리는 배포 작업 -> 상용버전 중지시간 및 사람에 의한 에러 -> 배포를 하는 것에 자신감이 없어진 팀 -> 더 적은 배포와 기능들

-## ![✔] 5.17. Node.js의 LTS 릴리즈 버전 사용 +## ![✔] 5.17. Node.js의 LTS 릴리즈 버전을 사용해라 **핵심요약:** LTS 버전의 Node.js를 사용하여 중요한 버그 수정, 보안 업데이트 및 성능 향상을 받아라 @@ -693,7 +726,7 @@ null == undefined // true ## ![✔] 5.18. 앱 내에서 로그를 라우팅하지 말라 -**핵심요약:** 로그의 목적지는 개발자가 어플리케이션 코드에 하드코딩해서는 안되고 프로그램이 실행되는 실행환경에서 정의되어야 한다. 개발자는 로거 유틸리티를 이용하여 로그를 `stdout`에 작성하고 상용환경(컨테이너, 서버 등)이 해당 `stdout`스트림을 적절한 목적지로 파이프해야한다 +**핵심요약:** 로그의 목적지는 개발자가 어플리케이션 코드에 하드코딩해서는 안되고, 대신 프로그램이 실행되는 실행환경에서 정의되어야 한다. 개발자는 로거 유틸리티를 이용하여 로그를 `stdout`에 작성하고 상용환경(컨테이너, 서버 등)이 해당 `stdout`스트림을 적절한 목적지(즉 Splunk, Graylog, ElasticSearch 등)로 수송해야한다 **그렇게 하지 않을 경우:** 어플리케이션 로그 라우팅 처리 === 확장성 저하, 로그 유실, 관심사의 분리 실패(Separation of Concerns, SoC) @@ -723,292 +756,292 @@ null == undefined // true -**핵심요약:** 이왕이면 코드를 작성하면서, 가능한한 빨리 eslint-plugin-security와 같은 보안 관련 linter 플러그인을 사용하여 보안 취약점을 잡으십시오. 이것은 eval, 자식 프로세스 호출, string iteral을 이용한 모듈 import (예를 들면 유저 인풋) 같은 보안 취약점을 잡는데 도움이 될 수 있다. 보안 linter가 잡는 코드를 보려면, 아래의 'Read more'을 클릭하십시오. +**핵심요약:** [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security)와 같은 보안 관련 linter 플러그인을 사용하여 보안 취약점과 문제점을 가능한 한 빨리 잡아라. 이것은 eval 사용, 자식 프로세스 호출, string literal(예: 사용자 입력)을 쓰는 모듈 불러오기 같은 보안 취약점을 잡는데 도움이 될 수 있다. 보안 linter가 잡는 코드를 보려면, 아래의 '자세히 보기'을 클릭해라. -**그렇게 하지 않을 경우:** 개발 중에 직접 보안 취약점이 도리 수 있었던 것이 프로덕션에서 주요한 이슈가 된다. 또, 프로젝트가 일관된 보안 프렉티스를 따르지 않아, 취약점이 노출되거나 민감한 정보가 원격 저장소에 유출될 수 있다. +**그렇게 하지 않을 경우:** 개발과정에서는 이해하기 쉬운 보안 약점이었음에도 상용환경에서는 주요쟁점이 된다. 또, 프로젝트가 일관된 보안 프렉티스를 따르지 않아, 취약점이 노출되거나 민감한 정보가 원격 저장소에 유출될 수 있다. 🔗 [**자세히 보기: Lint rules**](/sections/security/lintrules.md)

-## ![✔] 6.2. Limit concurrent requests using a middleware +## ![✔] 6.2. 미들웨어로 병행연산 (concurrent) 요청들을 제한해라 -**핵심요약:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) +**핵심요약:** DOS 서비스 거부 공격은 흔하며 하기에도 비교적 쉽다. 클라우드 로드밸런서, 클라우드 방화벽, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) 패키지나, (소규모의 덜 중요한 앱의 경우) 비율제한 미들웨어(예: [express-rate-limit](https://www.npmjs.com/package/express-rate-limit))를 써서 비율제한을 시행해라. -**그렇게 하지 않을 경우:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. +**그렇게 하지 않을 경우:** 애플리케이션이 서비스 거부 공격을 받으면 실제 이용자들이 받는 서비스가 저하되거나 먹통이 된다. 🔗 [**자세히 보기: Implement rate limiting**](/sections/security/limitrequests.md)

-## ![✔] 6.3 Extract secrets from config files or use packages to encrypt them +## ![✔] 6.3 기밀은 설정 파일에서 빼거나 패키지를 이용해서 암호화해라 -**핵심요약:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally +**핵심요약:** 기밀사항은 절대 평문 형태로 설정 파일이나 소스코드에 저장하지 말아라. 그 대신 Vault나 Kubernetes/Docker Secrets나 환경변수 같은 기밀사항 관리 시스템을 써라. 부득이하게 소스 콘트롤에 기밀사항을 저장해야 하는 경우, 반드시 암호화해서 관리해라 (rolling keys, 만료, 감사 등). pre-commit/push hook을 사용해서 실수로 기밀사항을 버젼 콘트롤에 커밋하는 것을 막아라. -**그렇게 하지 않을 경우:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). +**그렇게 하지 않을 경우:** 비공개 저장소라 하여도 소스 제어가 실수로 공개되면 모든 기밀사항이 그대로 드러난다. 외부관계자가 소스제어에 접근이 가능해지면 의도치 않아도 관련 시스템(데이터베이스, API, 서비스 등)에도 접근을 허락하는것과 마찬가지다. 🔗 [**자세히 보기: Secret management**](/sections/security/secretmanagement.md)

-## ![✔] 6.4. Prevent query injection vulnerabilities with ORM/ODM libraries +## ![✔] 6.4. ORM/ODM 라이브러리를 사용해 쿼리 주입 공격을 막아라 -**핵심요약:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. +**핵심요약:** SQL/NoSQL 주입과 같은 악의적인 공격을 막기 위해서는 ORM/ODM을 쓰거나, 데이터를 항상 이스케이프 처리 해 주거나 named or indexed parameterized queries를 지지하고, expected data type의 사용자 입력을 확인해주는 데이터베이스 라이브러리를 항상 활용해라. 자바스크립트 템플릿 문자열(template strings)이나 문자열 병합(string concatenation)으로 값을 주입하면 앱을 광범위한 취약점에 노출시키므로 절대 그대로 써서는 안된다. 평판이 좋은 Node.js 데이터 접근 라이브러리들(예: [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose))은 모두 주입 공격을 막아주는 보호대책이 내장되어있다. -**그렇게 하지 않을 경우:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. +**그렇게 하지 않을 경우:** 확인되지 않거나 (unvalidated) 살균되지 않은 (unsanitized) 사용자 입력은 MangoDB for NoSQL를 쓸 때 operator injection를 초래할 수 있고, 제대로 된 위생처리 시스템이나 ORM을 쓰지 않는것은 SQL 주입 공격을 쉽게 만들어 엄청난 취약점을 만들어낼 수 있다. 🔗 [**자세히 보기: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md)

-## ![✔] 6.5. Collection of generic security best practices +## ![✔] 6.5. 일반적인 보안 모범사례 모음 -**핵심요약:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. +**핵심요약:** 이것은 Node.js와는 직접적으로 관련되지 않은 보안과 관련된 조언 모음집이지만, Node의 시행도 다른 언어들과 그닥 다르지 않다. 자세히 보기를 클릭해서 읽어봐라. 🔗 [**자세히 보기: Common security best practices**](/sections/security/commonsecuritybestpractices.md)

-## ![✔] 6.6. Adjust the HTTP response headers for enhanced security +## ![✔] 6.6. HTTP 응답 헤더를 조정해서 보안을 강화해라 -**핵심요약:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). +**핵심요약:** 보안 헤더를 사용해서 교차사이트 스트립팅 (XSS), 클릭재킹과 같이 자주 있는 악의적인 공격들을 막아라. [helmet](https://www.npmjs.com/package/helmet)과 같은 모듈을 사용하면 쉽게 설정할 수 있다. -**그렇게 하지 않을 경우:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities +**그렇게 하지 않을 경우:** 공격자들이 애플리케이션 사용자들을 직접적으로 공격할 수 있게되면 엄청난 보안 취약점을 초래할 수 있다 🔗 [**자세히 보기: Using secure headers in your application**](/sections/security/secureheaders.md)

-## ![✔] 6.7. Constantly and automatically inspect for vulnerable dependencies +## ![✔] 6.7. 끊임없이 자동적으로 취약한 의존성은 없는지 검사해라 -**핵심요약:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. +**핵심요약:** npm 생태계의 프로젝트들은 의존성이 여럿 있는것이 보통이다. 의존성들은 새로운 취약점들이 발견 될 때마다 고쳐야 한다. [npm audit](https://docs.npmjs.com/cli/audit)이나 [snyk](https://snyk.io/) 같은 도구들을 이용해 취약한 의존성을 감시하고 패치해라. 이런 도구들을 CI 체재와 통합시켜 취약한 의존성이 상용버젼까지 새어 나가기 전에 잡아라 -**그렇게 하지 않을 경우:** An attacker could detect your web framework and attack all its known vulnerabilities. +**그렇게 하지 않을 경우:** 공격자가 당신의 웹 프레임워크를 감지하면 알려진 모든 취약점을 공격할 수 있다. 🔗 [**자세히 보기: Dependency security**](/sections/security/dependencysecurity.md)

-## ![✔] 6.8. Avoid using the Node.js crypto library for handling passwords, use Bcrypt +## ![✔] 6.8. 암호를 처리할 때는 Node.js의 암호 라이브러리 대신 Bcrypt를 써라 -**핵심요약:** Passwords or secrets (API keys) should be stored using a secure hash + salt function like `bcrypt`, that should be a preferred choice over its JavaScript implementation due to performance and security reasons. +**핵심요약:** 암호나 기밀사항(API keys)들은 `bcrypt` 같은 보안 해시+솔트 함수를 쓰는것이 자바스크립트 implementation을 쓰는 것보다 보안이나 성능면에서 더 낫다. -**그렇게 하지 않을 경우:** Passwords or secrets that are persisted without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. +**그렇게 하지 않을 경우:** 보안함수(secure function)를 쓰지 않고 저장된 암호나 기밀사항들은 억지기법이나 사전공격에 취약해져 결국엔 다 뚫리게 된다. 🔗 [**자세히 보기: Use Bcrypt**](/sections/security/bcryptpasswords.md)

-## ![✔] 6.9. Escape HTML, JS and CSS output +## ![✔] 6.9. HTML, JS and CSS 출력은 이스케이프 해라 -**핵심요약:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) +**핵심요약:** 신뢰할 수 없는 데이터가 브라우져에 보내져 보여지기만 하는게 아니라 실행이되는것을 교차 사이트 스크립팅 (XSS) 이라고 한다. 데이터를 명백하게 절대 실행되어서는 안되는 콘텐트라고 (즉 엔코딩이라던가 이스케이프한다던가) 표시하는 것 전용 라이브러리를 써서 이것을 완화시켜라 -**그렇게 하지 않을 경우:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients +**그렇게 하지 않을 경우:** 공격자가 악의적인 자바스크립트 코드를 당신 DB에 저장하면 불쌍한 클라이언트에게 그대로 보내질 수 있다 🔗 [**자세히 보기: Escape output**](/sections/security/escape-output.md)

-## ![✔] 6.10. Validate incoming JSON schemas +## ![✔] 6.10. 들어오는 JSON 스키마를 검사해라 -**핵심요약:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) +**핵심요약:** 들어오는 요청들의 body payload를 검사하여 기대에 부응하지는지 확인하고, 하지 않을경우 실패시켜버려라. [jsonschema](https://www.npmjs.com/package/jsonschema)나 [joi](https://www.npmjs.com/package/joi) 같은 JSON-기반의 경량의 validation schema를 쓰면 성가시게 매 경로마다 검사를 코딩해야 하는 일을 피할 수 있다. -**그렇게 하지 않을 경우:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application +**그렇게 하지 않을 경우:** 입력에 대해 너그럽고 관대하면 공격 받을 수 있는 면적이 넓어지고, 공격자에게 애플리케이션을 끌어내리는 조합을 찾을때까지 다양한 입력값을 시도해보도록 장려하는 꼴이 된다. 🔗 [**자세히 보기: Validate incoming JSON schemas**](/sections/security/validation.md)

-## ![✔] 6.11. Support blacklisting JWTs +## ![✔] 6.11. JWT 블랙리스트를 지원해라 -**핵심요약:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blacklist of untrusted tokens that are validated on each request. +**핵심요약:** JSON Web Token(이를테면 [Passport.js](https://github.com/jaredhanson/passport)같은)을 쓰는 경우, 기본적으로 발행된 토큰의 권한을 취소하는 메커니즘은 없다. 사용자가 악의적인 행동을 한다 해도 유효한 토큰이 있는한 막을 길이 없다. 신뢰할 수 없는 토큰 블랙리스트를 만들어 매 요청마다 검사해서 완화시켜라. -**그렇게 하지 않을 경우:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. +**그렇게 하지 않을 경우:** 제삼자가 만료되었거나 부적절한 토큰을 악의적으로 사용하여 애플리케이션에 접근하거나 토큰 소유자인 척 가장할 수 있다. 🔗 [**자세히 보기: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md)

-## ![✔] 6.12. Prevent brute-force attacks against authorization +## ![✔] 6.12. 권한부여 억지기법 공격을 예방해라 -**핵심요약:** A simple and powerful technique is to limit authorization attempts using two metrics: - -1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. -2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. +**핵심요약:** 간단하지만 강력한 테크닉은 다음 두가지를 측량하여 권한부여 (authorization) 시도를 제한하는 것이다: + +1. 첫째는 같은 사용자의 고유 아이디/이름과 IP주소로부터의 연이은 실패 시도 갯수 +2. 두번째는 오랜 시간 동안 같은 IP 주소에서부터의 실패 시도 갯수. 예를 들면, 같은 IP 주소로부터 하루 사이 실패 시도가 100개라면 차단해버려라. -**그렇게 하지 않을 경우:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application +**그렇게 하지 않을 경우:** 공격자가 무제한으로 암호를 시도해서 특권있는 계좌에 접근할 수 있다 🔗 [**자세히 보기: Login rate limiting**](/sections/security/login-rate-limit.md)

-## ![✔] 6.13. Run Node.js as non-root user +## ![✔] 6.13. Node.js를 루트가 아닌 사용자로써 실행하라 -**핵심요약:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" +**핵심요약:** Node.js가 승인에 제한이 없는 루트 사용자로써 실행하는 일이 비일비재하다. 예를 들면, 도커 컨테이너 안에서는 이것이 기본적인 설정이다. 루트가 아닌 사용자를 만들어 도커 이미지에 구워넣거나 (아래예시) 프로세스를 "-u username" 플래그를 써서 이 사용자를 대신해서 실행하는것을 추천한다. -**그렇게 하지 않을 경우:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to his server) +**그렇게 하지 않을 경우:** 공격자가 서버에 스크립트를 실행하는데 성공하면 로컬 머신을 마음대로 할 권리를 무제한으로 갖게 된다 (예: iptable을 바꾼다던가 트래픽 경로를 자기 서버로 변경 한다던가) 🔗 [**자세히 보기: Run Node.js as non-root user**](/sections/security/non-root-user.md)

-## ![✔] 6.14. Limit payload size using a reverse-proxy or a middleware +## ![✔] 6.14. 리버스 프록시나 미들웨어를 사용해서 페이로드 크기를 제한해라 -**핵심요약:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads +**핵심요약:** 페이로드 사이즈가 클수록, 단일 스레드가 이것을 처리하는데 더 열심히 일해야 한다. 이것은 공격자들이 엄청난 양의 요청(DOS/DDOS 공격)을 쓰지 않고도 서버를 끌어내릴 수 있는 기회를 제공한다. 가장자리(예: 방화벽, ELB)에서 들어오는 요청들의 바디 크기를 제한하거나, 소규모의 페이로드만 받아들이도록 [express body parser](https://github.com/expressjs/body-parser)를 설정해라 -**그렇게 하지 않을 경우:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks +**그렇게 하지 않을 경우:** 애플리케이션이 커다란 요청을 처리해야하면 다른 중요한 일을 완수할 수 없게 되어 성능이 영향을 받고 DOS 공격에 취약하게 된다 🔗 [**자세히 보기: Limit payload size**](/sections/security/requestpayloadsizelimit.md)

-## ![✔] 6.15. Avoid JavaScript eval statements +## ![✔] 6.15. 자바스크립트 eval statement를 피하라 -**핵심요약:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. +**핵심요약:** `eval`은 임의적인 자바스크립트 코드를 런타임시 실행하는것을 허용하기에 사악하다. 이는 성능면에서 문제가 될 뿐 아니라 사용자 입력을 근원으로 한 악의적인 자바스크립트 코드 때문에 보안면에서도 문제가 된다. 피해야 할 또다른 언어 특색으로는 `new Function` constructor 생성자가 있다. `setTimeout`와 `setInterval` 또한 절대로 동적이 자바스크립트 코드를 주어서는 안된다. -**그렇게 하지 않을 경우:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. +**그렇게 하지 않을 경우:** 악의적인 자바스크립트 코드가 `eval`같이 실시간으로 평가하는 자바스크립트 언어 함수에 넘어가면 페이지 안의 자바스크립트 승인을 완전히 장악하게 된다. 이 취약점은 XSS 공격 형태로 자주 나타난다. 🔗 [**자세히 보기: Avoid JavaScript eval statements**](/sections/security/avoideval.md)

-## ![✔] 6.16. Prevent evil RegEx from overloading your single thread execution +## ![✔] 6.16. 악성 정규표현(RegEx)이 단일 스레드 실행을 과부하시키는것을 막아라 -**핵심요약:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns +**핵심요약:** 정규표현은 유용하긴 하지만 자바스크립트 애플리케이션, 특히나 Node.js 플랫폼의 경우 전체적으로 위협을 가한다. 사용자 입력 텍스트를 비교하여 처리하는데는 엄청난 양의 CPU 사이클이 요구된다. 단어 10개를 검사하는 단 하나의 요청이 이벤트 루프 전체를 6초동안 정체시키고 CPU를 🔥지를 만큼 RegEx 처리는 비효율적이다. 이 때문에,직접 정규표현 패턴을 작성하기 보다는 [validator.js](https://github.com/chriso/validator.js) 같은 써드파티 검사 패키지를 쓰거나, [safe-regex](https://github.com/substack/safe-regex)를 써서 취약한 정규표한 패턴을 감지해라 -**그렇게 하지 않을 경우:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 +**그렇게 하지 않을 경우:** 저조하게 쓰여진 정규표현들은 이벤트 루프를 완전히 정체시킬 수 있는 정규표현 DOS 공격에 취약해진다. 예를들면, 자주 쓰이는 `moment` 패키지 또한 2017년 11월에 악성 정규표현 사용에 취약하다는 것이 발견되었다 🔗 [**자세히 보기: Prevent malicious RegEx**](/sections/security/regex.md)

-## ![✔] 6.17. Avoid module loading using a variable +## ![✔] 6.17. 변수를 사용해 모듈을 로딩하는것을 피해라 -**핵심요약:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough +**핵심요약:** 매개변수가 사용자 입력이 원천지일 염려가 있으므로 다른 파일을 파라메터 형태의 경로로 require/import 하는것은 피해라. 이건 일반적으로 사용자 입력을 원천지로 한 다른 동적 변수로 파일이나 (즉 `fs.readFile()`) 다른 민감한 리소스에 접근할 때도 해당한다. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) 린터를 쓰면 이런 패턴들을 초반에 잡아내어 경고해준다 -**그렇게 하지 않을 경우:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the filesystem, or access already existing system files. +**그렇게 하지 않을 경우:** 악의적인 사용자 입력이 이전에 파일시스템에 업로드되었거나 이미 시스템에 존재하는 흑화된 파일을 require 하는데 쓰이는 매개변수로 들어가게 될 수 있다. 🔗 [**자세히 보기: Safe module loading**](/sections/security/safemoduleloading.md)

-## ![✔] 6.18. Run unsafe code in a sandbox +## ![✔] 6.18. 위험한 코드는 샌드박스 안에서 실행해라 -**핵심요약:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox +**핵심요약:** 런타임에 주어진 외부 코드(예: 플러그인)를 실행해야 하는 경우, 메인 코드를 플러그인으로부터 격리시켜 보호하는 샌드박스 실행 환경을 무엇이든 써라. 전용 프로세스나 (예: `cluster.fork()`), 서버리스 환경이나 샌드박스 역할을 하는 전용 npm 패키지를 사용하면 달성할 수 있다. -**그렇게 하지 않을 경우:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables +**그렇게 하지 않을 경우:** 플러그인이 무한 루프나, 메모리 과부화나, 민감한 프로세스 환경 변수로의 접근과 같이 무한히 가지각색으로 공격할 수 있다 🔗 [**자세히 보기: Run unsafe code in a sandbox**](/sections/security/sandbox.md)

-## ![✔] 6.19. Take extra care when working with child processes +## ![✔] 6.19. 자식 프로세스를 다룰 땐 특히 조심해라 -**핵심요약:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. +**핵심요약:** 자식 프로세스를 쓰는것은 가능한 한 피하고, 부득이하게 써야 한다면 입력은 검사하고 무결처리해서 shell 주입 공격을 완화시켜라. 정의상 속성 집합이 있는 단일 명령어만 수행하고 shell 매개변수 확장을 허가하지 않는 `child_process.execFile` 쓰는것을 선호해라. -**그렇게 하지 않을 경우:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. +**그렇게 하지 않을 경우:** 순진무구하게 자식 프로세스를 썼다가는 악의적인 사용자 입력이 무결처리 되지 않은 시스템 명령어로 인한 원격 커맨드 실행이나 쉘 주입 공격을 초래할 수 있다. 🔗 [**자세히 보기: Be cautious when working with child processes**](/sections/security/childprocesses.md)

-## ![✔] 6.20. Hide error details from clients +## ![✔] 6.20. 에러 세부사항은 클라이언트로부터 숨겨라 -**핵심요약:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details +**핵심요약:** 통합된 express 에러 핸들러는 기본적으로 에러 세부사항을 감춘다. 허나, 당신이 커스텀 에러 처리 로직을 만들어 쓰고 있을 가능성이 높다 (많은이들이 이게 모범사례라고 여긴다). 그럴 경우 민감한 애플리케이션 세부사항을 포함할 수 있는 에러 객체를 절대로 통째로 클라이언트에 보내주지 않도록 해라 -**그렇게 하지 않을 경우:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace +**그렇게 하지 않을 경우:** 서버 파일 경로나, 사용중인 제삼자 모듈, 그리고 그 외 애플리케이션 내부 작업 순서 같이 공격자가 악용할 수 있는 민감한 애플리케이션 세부사항이 스택트레이스에 보여지는 정보로 새어나갈 수 있다 🔗 [**자세히 보기: Hide error details from client**](/sections/security/hideerrors.md)

-## ![✔] 6.21. Configure 2FA for npm or Yarn +## ![✔] 6.21. npm이나 yarn 이중인증(2FA)을 설정해라 -**핵심요약:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. +**핵심요약:** 개발 과정의 모든 단계는 다중인증(MFA: multi-factor authentication)을 사용해서 보호해야한다. npm이나 Yarn은 공격자들이 개발자 암호를 수집할 수 있는 절호의 기회다. 공격자들은 개발자의 크리덴셜을 사용하면 악의적인 코드를 다양한 프로젝트와 서비스에 널리 설치된 라이브러리에 주입할 수 있다. 공개 출판 되었다면 웹상에서도 가능하다. npm 이중인증을 활성화 해 두면 공격자들이 패키지 코드를 변경할 확률이 0에 가까워진다. -**그렇게 하지 않을 경우:** [Have you heard about the eslint developer who's password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) +**그렇게 하지 않을 경우:** [암호를 하이재킹 당한 eslint 개발자에 대해 들어봤는가?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8)

-## ![✔] 6.22. Modify session middleware settings +## ![✔] 6.22. 세션 미들웨어 설정을 수정해라 -**핵심요약:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) +**핵심요약:** 웹 프레임워크나 장비는 각각 고유의 약점이 있다-공격자에게 어떤 웹 프레임워크를 쓰는지 알려주는 것은 그들을 돕는 꼴이 된다. 세션 미들웨어 기본설정을 쓰는 것은 `X-Powered-By` 헤더같이 모듈이나 프레임워크 관련 하이재킹 공격에 앱이 취약해지게 만든다. 테크 스택의 신분(예: Node.js, express)을 드러내는 것은 일단 감추고 봐라 -**그렇게 하지 않을 경우:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities +**그렇게 하지 않을 경우:** 쿠키가 insecure 연결로 송신되면 공격자가 세션 ID를 보고 웹 어플리케이션 프레임워크를 식별하고, 모듈관련 취약점도 찾아낼 수 있다 🔗 [**자세히 보기: Cookie and session security**](/sections/security/sessions.md)

-## ![✔] 6.23. Avoid DOS attacks by explicitly setting when a process should crash +## ![✔] 6.23. 프로세스가 언제 충돌해야 하는지 명백히 해서 DOS 공격을 피해라 -**핵심요약:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) +**핵심요약:** Node 프로세스는 에러가 처리되지 않으면 충돌한다. 많은 모범사례들이 에러를 받아 처리했음에도 exit 하는것을 권장한다. Express로 예를 들자면, 비동기 에러가 발생한 경우, route를 catch clause로 감싸지 않는 한 항상 충돌로 이어진다. 이건 어떤 입력이 프로세스를 충돌시키는지 아는 공격자들이 같은 요청을 계속 보내는 공격을 할 수 있게 만들어 버린다. 이것을 바로 고칠 수 있는 방안은 없지만, 통증을 완화시킬 수 있는 방법이 몇 개 존재한다: 프로세스가 처리되지 않은 에러로 인해 충돌할 때마다 치명적인 심각성으로 경보를 발하고, 입력을 검사하며 무효한 사용자 입력으로 인한 충돌을 피하고, 모든 route들을 catch로 감싸고, 에러의 원인지가 요청이면 충돌하지 않는것을 고려해라 (에러가 global하게 나는것과 반대로) -**그렇게 하지 않을 경우:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease +**그렇게 하지 않을 경우:** 경험에서 우러난 추측이긴 하지만: Node.js 애플리케이션이 여럿 있을때, 모든 POST 요청에 빈 JSON body를 붙여 보내면 그중 애플리케이션이 몇 개 충돌한다. 그 시점으로부터는 계속 같은 요청을 보내면 나머지 애플리케이션들을 쉽게 흐너뜨릴 수 있다.

-## ![✔] 6.24. Prevent unsafe redirects +## ![✔] 6.24. 위험한 리디렉트는 막아라 -**핵심요약:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. +**핵심요약:** 사용자 입력을 검사하지 않는 리디렉트는 공격자가 피싱 사기나 사용자 자격 절도와 같이 악의적인 공격을 할 수 있게 한다. -**그렇게 하지 않을 경우:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. +**그렇게 하지 않을 경우:** 공격자가 당신이 외부 유저로부터 조달된 입력을 검사하지 않는다는 것을 발견 할 경우, 특수 조작된 포럼 링크를 포럼이나 소셜 미디어, 그 외 공공 장소에 게재해서 유저들이 클릭하도록 해서 이 취약점을 악용할 수 있다. 🔗 [**자세히 보기: Prevent unsafe redirects**](/sections/security/saferedirects.md)

-## ![✔] 6.25. Avoid publishing secrets to the npm registry +## ![✔] 6.25. 기밀사항을 npm registry에 출판하는것을 막아라 -**핵심요약:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. +**핵심요약:** 공공 npm 레지스트리에 기밀사항을 실수로 발행하는 것을 미리 예방해라. `.npmignore` 파일을 사용해서 특정 파일이나 폴더를 블랙리스트하거나, `package.json` 안의 `files` 배열이 화이트리스트 역할을 할 수 있다. -**그렇게 하지 않을 경우:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. +**그렇게 하지 않을 경우:** 프로젝트의 API key나, 암호나, 그 외 기밀사항들이 이것들을 우연히 발견한 이들 누구나에 의해 남용되면 재정적 손실이나 사칭과 같은 위험을 초래할 수 있다. 🔗 [**자세히 보기: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md)


@@ -1017,26 +1050,26 @@ null == undefined // true # `7. 초안: 성능` -## 협력자들이 현재 작업중입니다. [함꼐 하시겠습니까?](https://github.com/i0natan/nodebestpractices/issues/256) +## 협력자들이 현재 작업중입니다. [함께 하시겠습니까?](https://github.com/i0natan/nodebestpractices/issues/256)

-## ![✔] 7.1. Don't block the event loop +## ![✔] 7.1. 이벤트 루프를 막지 말아라 -**핵심요약:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. +**핵심요약:** CPU 집약적인 과제들은 거의 단일 스레드로 된 이벤드 루프를 블로킹하고 전용 스레드나 프로세스, 혹은 컨텍스트에 따라 그 외 다른 기술에 떠넘기므로 피하라. -**그렇게 하지 않을 경우:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** +**그렇게 하지 않을 경우:** 이벤트 루프가 블로킹되면 Node.js는 다른 요청을 처리할 수 없게 되어 동시성 (concurrent) 사용자들을 지체하게 한다. **사용자 3000명이 응답을 기다리고 있고, 콘텐츠도 제공될 준비가 되어있는데, 단 하나의 요청이 서버가 결과물을 발송하지 못하도록 블로킹 할 수 있다** 🔗 [**자세히 보기: Do not block the event loop**](/sections/performance/block-loop.md)


-## ![✔] 7.2. Prefer native JS methods over user-land utils like Lodash +## ![✔] 7.2. Lodash같은 user-land 유틸 대신 네이티브 자바스크립트 메소드를 택해라 -**핵심요약:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. -Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. +**핵심요약:** 네이티브 메소드 대신 `lodash` 나 `underscore` 같은 유틸 라이브러리를 쓰는 것은 불필요한 의존성이나 성능 저하를 야기할 수 있기에 보통 페날티가 붙는다. +새로운 V8 엔진과 함께 새로운 ES 기준이 도입되고부터 네이티브 메소드가 유틸리티 라이브러리보다 50% 더 능률적이라는 것을 명심해라. -**그렇게 하지 않을 경우:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. +**그렇게 하지 않을 경우:** 기본적으로 **이미** 내장된 코드를 쓰거나 코드를 몇줄 더 써서 파일을 몇개 더 써야 하는 것을 막을 수 있었음에도 불구하고 더 비능률적인 프로젝트를 유지해야 할 것이다. 🔗 [**자세히 보기: Native over user land utils**](/sections/performance/nativeoverutil.md) @@ -1054,75 +1087,75 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ### 번역 작업 완료 -- ![BR](/assets/flags/BR.png) [Brazilian Portuguese](./README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) -- ![CN](/assets/flags/CN.png) [Chinese](./README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) -- ![RU](/assets/flags/RU.png) [Russian](./README.russian.md) - Courtesy of [Alex Ivanov](https://github.com/contributorpw) +- ![BR](/assets/flags/BR.png) [브라질식 포르투갈어](./README.brazilian-portuguese.md) - [마르셀로 멜로](https://github.com/marcelosdm) 제공 +- ![CN](/assets/flags/CN.png) [중국어](./README.chinese.md) - [맷 진](https://github.com/mattjin) 제공 +- ![RU](/assets/flags/RU.png) [러시아어](./README.russian.md) - [알렉스 이바노브](https://github.com/contributorpw) 제공 ### 번역 작업중 -- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [프랑스어](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) 히브리어 ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [한국어](README.korean.md) - [한상범](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) 제공 +- ![ES](/assets/flags/ES.png) [스페인어](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) 터키어 ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139))

## 운영 위원회 -Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/i0natan/nodebestpractices/projects). +프로젝트를 지도하고 앞으로 나아갈 방향을 제시하는데 함께 일하는 운영 위원회 일원들을 소개합니다. 추가로, 위원회원들은 각자 [Github 프로젝트](https://github.com/i0natan/nodebestpractices/projects) 아래에 인행되는 프로젝트들을 인솔합니다. -[Yoni Goldberg](https://github.com/i0natan) +[요니 골드버그](https://github.com/i0natan) -Independent Node.js consultant who works with customers in the USA, Europe, and Israel on building large-scale Node.js applications. Many of the best practices above were first published at [goldbergyoni.com](https://goldbergyoni.com). Reach Yoni at [@goldbergyoni](https://github.com/goldbergyoni) or [me@goldbergyoni.com](mailto:me@goldbergyoni.com) +미국, 유럽과 이스라엘의 고객들과 대규모 Node.js 애플리케이션 건축하는데 일하는 독립적인 Node.js 컨설턴트입니다. 모범사례 중 다수는 [goldbergyoni.com](https://goldbergyoni.com)에 처음 게재되었던 것들입니다. [@goldbergyoni](https://github.com/goldbergyoni)나 [me@goldbergyoni.com](mailto:me@goldbergyoni.com)로 요니에게 연락하세요.
-[Bruno Scheufler](https://github.com/BrunoScheufler) +[브루노 슈플러](https://github.com/BrunoScheufler) -💻 full-stack web engineer, Node.js & GraphQL enthusiast +💻 풀스택 웹 엔지니어, Node.js & GraphQL 광팬
-[Kyle Martin](https://github.com/js-kyle) +[카일 마틴](https://github.com/js-kyle) -Full Stack Developer & Site Reliability Engineer based in New Zealand, interested in web application security, and architecting and building Node.js applications to perform at global scale. +뉴질랜드에 거주하는 풀스택 개발자 & 사이트 신뢰성 엔지니어이며, 웹 애플리케이션 보안과 글로벌한 규모에서 수행하는 Node.js 애플리케이션을 설계하고 건축하는데 관심이 있습니다.
-[Sagir Khan](https://github.com/sagirk) +[사기르 칸](https://github.com/sagirk) -Deep specialist in JavaScript and its ecosystem — React, Node.js, MongoDB, pretty much anything that involves using JavaScript/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation, collaborating on the Community Committee's Website Redesign Initiative. +웹 플랫폼을 사용해 세계적으로 유명한 브랜드들을 위한 상품을 만드는 자바스크립트 및 생태계 (React, Node.js, MongoDB 외 시스템 어느 계층에서건 자바스크립트/JSON 사용과 관련된 것은 어떤것이든) 전문가 입니다. Node.js 단체의 개인 일원이며, 공동체 위원회의 웹사이트 재설계 계획에 협력하고 있습니다.
-## 협력자 +## 공동 저자 -Thank you to all our collaborators! 🙏 +모든 공동 저자 분들께 감사드립니다! 🙏 -Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 +공동 저자들은 새로운 모범사례를 제안하거나, 사안을 분류하거나, 풀리퀘스트를 검토하는 등 리포지토리에 정기적으로 기여하는 일원들입니다. 수천명의 사람들이 더 나은 Node.js 애플리케이션을 만들 수 있도록 안내하며 돕는데 관심이 있으시다면 [기여자 지침서](/.operations/CONTRIBUTING.md)를 읽어주세요 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :----------------------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------: | +| [이도 릭터 (창립주)](https://github.com/idori) | [키스 홀리데이](https://github.com/TheHollidayInn) | [케빈 브뤼예르](https://github.com/kevynb) | -### 구 협력자 +### 전 공동 저자 | | | :-------------------------------------------------------------------------------------------------------------------------: | @@ -1130,8 +1163,8 @@ Our collaborators are members who are contributing to the repository on a regula
-## Contributing -오픈소스에 참여하고 싶으시다면 지금이 바로 기회! [contributing docs](.operations/CONTRIBUTING.md)에서 더 자세한 내용을 확인하세요. +## 기여하기 +오픈소스에 참여하고 싶으시다면 지금이 바로 기회! [기여자 지침서](.operations/CONTRIBUTING.md)에서 더 자세한 내용을 확인하세요. ## 기여자 ✨ From 4a1440efd6c94d41a564d9a49a159ada97c30f55 Mon Sep 17 00:00:00 2001 From: "A.Song" Date: Sun, 5 Apr 2020 03:05:31 -0700 Subject: [PATCH 0269/1795] Translate breakintcomponents.korean.md - Remove from breakintcomponents.korean.md --- .../breakintcomponents.korean.md | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/sections/projectstructre/breakintcomponents.korean.md b/sections/projectstructre/breakintcomponents.korean.md index 5f42d25c6..7912668ef 100644 --- a/sections/projectstructre/breakintcomponents.korean.md +++ b/sections/projectstructre/breakintcomponents.korean.md @@ -1,37 +1,37 @@ -# Structure your solution by components - -

- -### One Paragraph Explainer - -For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows. - -

- -### Blog Quote: "Scaling requires scaling of the entire application" - - From the blog MartinFowler.com - -> Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource. - -

- -### Blog Quote: "So what does the architecture of your application scream?" - - From the blog [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html) - -> ...if you were looking at the architecture of a library, you’d likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.
- -So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?. - -

- -### Good: Structure your solution by self-contained components - -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") - -

- -### Bad: Group your files by technical role - -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +# 컴포넌트 기반으로 설계하라 + +

+ +### 한문단 설명 + +중소규모 이상의 앱부터는 단일암체 monolith는 정말 좋지 않다 - 의존성이 여럿 있는 커다란 하나의 소프트웨어를 쓰는 것은 꼬일대로 꼬인 스파게티 코드를 초래한다. 이 괴물을 '모듈화'하여 길들일만큼 숙련된 똑똑한 설계자들조차 디자인에 엄청난 정신력을 쏟아붓고, 모든 변화는 다른 의존하는 프로젝트에 어떤 영향을 미치는지 신중히 감정할것을 필요로 한다. 궁극적인 해결책은 소규모의 소프트웨어를 개발하는 것이다: 스택 전체를 다른이들과 파일을 공유하지 않는 자족적(self-contained)인 컴포넌트로 나누고, 추론하기 쉽게 극소수의 파일로만 구성되어야 한다 (예: API, 서비스, 데이터 접근, 테스트 등). 이것을 "마이크로서비스" 설계라고 부르기도 하는데, 마이크로서비스는 따라야 하는 사양이 아니라 원칙들이란 것을 아는 것이 중요하다. 원칙을 여럿 받아들여 완전한 마이크로서비스 설계를 할 수도 있고, 아니면 그 중 소수만 받아들일 수도 있다. 소프트웨어의 복잡하게 하지 않는 한 둘 다 좋은 방법이다. 최소한 컴포넌트 사이에 경계를 나누고, 프로젝트 최상위 루트에 각각의 비지니스 컴포넌트를 각기 다른 폴더에 배치하여 다른 컴포넌트들은 공용 인터페이스나 API로만 기능을 소비할 수 있게 자족적으로 만들 것. 이것이 컴포넌트를 간단하게 하고, 의존성 지옥을 방지하며 미래에 앱이 완전한 마이크로서비스로 자라나는 길을 닦는 기반이다. + +

+ +### 블로그 인용: "확장하려면 애플리케이션 전체를 확장해야한다" + +MartinFowler.com 블로그로부터 + +> 단일암체 애플리케이션도 성공적일 수 있지만, 점점 더 많은 사람들이 불만을 느끼고 있다 - 특히 더 많은 애플리케이션들이 클라우드로 전개될수록. 변화 주기는 다 같이 묶여 있다 - 애플리케이션의 조그마한 부분을 바꾸면 단일암체 전체를 재건하고 재배치하여야 한다. 시간이 흐를수록 좋은 모듈식의 구조를 유지하는것이 힘들어지고, 모듈 하나에만 작용해야 할 변화가 그 모듈 이내에서만 작용하도록 하는것이 힘들어진다. 더 많은 자원을 필요로 하는 부분만 확장하는 것이 아니라, 확장하려면 애플리케이션 전체를 확장해야한다. + +

+ +### 블로그 인용: "그러니 당신의 어플리케이션의 설계를 보면 어떤 감이 오는가?" + +[uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html)블로그로부터 + +> ...도서관 설계도를 보면, 아마도 커다란 입구, 체크인/체크아웃 구역, 독서실, 소규모 회의실들, 도서관의 모든 책을 수용할 수 있게 책꽂이들을 놓을 만한 공간들이 보일 것이다. 설계도를 보면 도서관이라고 바로 감이 올 것이다.
+ +그러니 당신의 어플리케이션의 설계를 보면 어떤 감이 오는가? 최상위 디렉토리 구조와 최고위 레벨의 패키지의 소스 파일을 보면 건강 관리 시스템인지, 회계 시스템인지, 재고관리 시스템인지 바로 감이 오는가? 아니면 Rails이나 Spring/Hibernate, 혹은 ASP라는 감이 오는가?. + +

+ +### 좋은예: 자족적인 컴포넌트 기반으로 설계하라 + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") + +

+ +### 나쁜예: 파일을 기술적인 역할별로 모아라 + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") From f8523e7655e850745dfa41523bfb0ac6679e3a79 Mon Sep 17 00:00:00 2001 From: "A.Song" Date: Sun, 5 Apr 2020 03:14:17 -0700 Subject: [PATCH 0270/1795] Translate createlayers.korean.md --- .../projectstructre/createlayers.korean.md | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/sections/projectstructre/createlayers.korean.md b/sections/projectstructre/createlayers.korean.md index 029924a57..c6731556b 100644 --- a/sections/projectstructre/createlayers.korean.md +++ b/sections/projectstructre/createlayers.korean.md @@ -1,13 +1,13 @@ -# Layer your app, keep Express within its boundaries - -

- - ### Separate component code into layers: web, services, and DAL - -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") - -

- -### 1 min explainer: The downside of mixing layers - -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +# 컴포넌트를 계층화(layer)하고, Express를 그 경계 안에 둬라 + +

+ + ### 컴포넌트 코드를 웹, 서비스, 데이터 접근 언어(DAL) 계층으로 나누어라 + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") + +

+ +### 1분 설명: 계층을 섞으면 불리한 점 + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") From 00e67e835533be2f1670b9c763df942dbf07da21 Mon Sep 17 00:00:00 2001 From: "A.Song" Date: Sun, 5 Apr 2020 03:46:59 -0700 Subject: [PATCH 0271/1795] Translate wraputilities.korean.md --- README.korean.md | 2 +- .../projectstructre/wraputilities.korean.md | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.korean.md b/README.korean.md index 4bea8f4bc..d4f9d27c5 100644 --- a/README.korean.md +++ b/README.korean.md @@ -82,7 +82,7 @@

-## ![✔] 1.3 유틸리티들을 NPM 패키지로 감싸라(wrap) +## ![✔] 1.3 공유 유틸리티들은 NPM 패키지로 감싸라 (wrap) **핵심요약:** 커다란 코드 기반으로 구성되어있는 커다란 앱에서는 로깅, 암호화 같은 횡단 관심사(cross-cutting-concern)가 있는 유틸의 경우 당신이 쓴 코드로 감싸진 private NPM package의 형태로 노출이 되어야 한다. 이것은 여러 코드 기반과 프로젝트들에게 그것들을 공유할 수 있도록 해준다. diff --git a/sections/projectstructre/wraputilities.korean.md b/sections/projectstructre/wraputilities.korean.md index 4b52ff6db..f06ec4a1f 100644 --- a/sections/projectstructre/wraputilities.korean.md +++ b/sections/projectstructre/wraputilities.korean.md @@ -1,13 +1,13 @@ -# Wrap common utilities as npm packages - -

- -### One Paragraph Explainer - -Once you start growing and have different components on different servers which consumes similar utilities, you should start managing the dependencies - how can you keep 1 copy of your utility code and let multiple consumer components use and deploy it? well, there is a tool for that, it's called npm... Start by wrapping 3rd party utility packages with your own code to make it easily replaceable in the future and publish your own code as private npm package. Now, all your code base can import that code and benefit free dependency management tool. It's possible to publish npm packages for your own private use without sharing it publicly using [private modules](https://docs.npmjs.com/private-modules/intro), [private registry](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html) or [local npm packages](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc) - -

- -### Sharing your own common utilities across environments and components - -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") +# 공유 유틸리티들은 NPM 패키지로 감싸라 (wrap) + +

+ +### 한문단 설명 + +자라나기 시작하면서 비슷한 유틸리티들을 소비하는 다른 서버의 다른 컴포넌트들이 생겨나면 의존성을 관리하기 시작해야 한다 - 유틸리티 코드 한 부를 어떻게 소비자 컴포넌트 여럿이서 같이 쓰고 배치할 수 있게 하는가? 자, 여기 쓸만한 도구가 여기 있다...npm이라 불리는. 먼저 제삼자 유틸리티 패키지를 자신만의 코드로 감싸 미래에 대체하기 쉽게 하고 그 코드를 private npm 패키지로 publish해라. 이제 당신의 모든 코드 기반은 그 코드를 수입하여 무료 의존성 관리 도구의 혜택을 볼 수 있다. [private 모듈](https://docs.npmjs.com/private-modules/intro)이나 [private 레지스트리](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html), 혹은 [로컬 npm 패키지](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc)를 사용하면 npm 패키지를 공개적으로 공유하지 않고도 자용으로 쓸 수 있게 출판할 수 있다. + +

+ +### 당신만의 공유 유틸리티들을 환경과 컴포넌츠에 공유하기 + +![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") From 2e8f01b42ae495b589829213d1d867f985dfe280 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:34:28 +0000 Subject: [PATCH 0272/1795] docs: update README.md [skip ci] --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 2af73ce6c..56f1a667f 100644 --- a/README.md +++ b/README.md @@ -1297,7 +1297,6 @@ Thanks goes to these wonderful people who have contributed to this repository!
VinayaSathyanarayana

🖋
Kim Kern

🖋
Kenneth Freitas

🖋 -
songe

🖋 From af8121783bce1940d65e6feff2c05fb7013cd231 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:34:29 +0000 Subject: [PATCH 0273/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index cf05e7c3b..182bb4d21 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -871,7 +871,6 @@ ] }, { - "login": "songe", "name": "songe", "avatar_url": "https://avatars2.githubusercontent.com/u/1531561?v=4", From cd38576cafb7f385721eae67acd7665c84ceff9a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:38:39 +0000 Subject: [PATCH 0274/1795] docs: update README.md [skip ci] From 598f6730a3339a6618a02971aaa27bd43d1d297c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Apr 2020 16:38:40 +0000 Subject: [PATCH 0275/1795] docs: update .all-contributorsrc [skip ci] From d6fce31243fc326f2378998a86d455815a97c7ec Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 6 Apr 2020 19:39:48 +0300 Subject: [PATCH 0276/1795] Update README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 31658f57f..2a62e3244 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,6 @@ [![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) -
- -[:green_book: Comprehensive 10 hours course on Node.js testing & quality best practices](https://testjavascript.com/)
From 0c1d95cef521a70c26e5410867bc4c12f0b38bed Mon Sep 17 00:00:00 2001 From: Taylan Kasap Date: Mon, 20 Apr 2020 09:07:45 +0300 Subject: [PATCH 0277/1795] Fix typos --- README.md | 8 ++++---- sections/production/smartlogging.md | 4 ++-- sections/security/commonsecuritybestpractices.md | 2 +- sections/security/dependencysecurity.md | 2 +- sections/security/expirejwt.md | 6 +++--- sections/security/validation.md | 4 ++-- sections/testingandquality/aaa.md | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2a62e3244..02d1876e8 100644 --- a/README.md +++ b/README.md @@ -513,11 +513,11 @@ All statements above will return false if used with `===`

-## ![✔] 4.10 Use production-like env for e2e testing +## ![✔] 4.10 Use production-like environment for e2e testing -**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as closed to your real production as possible like a-continue +**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) -**Otherwise:** Without docker-compose teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments +**Otherwise:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments

@@ -946,7 +946,7 @@ All statements above will return false if used with `===` **TL;DR:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough -**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the filesystem, or access already existing system files. +**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. 🔗 [**Read More: Safe module loading**](/sections/security/safemoduleloading.md) diff --git a/sections/production/smartlogging.md b/sections/production/smartlogging.md index bcb7bc4af..5a8161e3e 100644 --- a/sections/production/smartlogging.md +++ b/sections/production/smartlogging.md @@ -8,9 +8,9 @@ Since you print out log statements anyway and you're obviously in a need of some **1. smart logging** – at the bare minimum you need to use a reputable logging library like [Winston](https://github.com/winstonjs/winston), [Bunyan](https://github.com/trentm/node-bunyan) and write meaningful information at each transaction start and end. Consider to also format log statements as JSON and provide all the contextual properties (e.g. user id, operation type, etc) so that the operations team can act on those fields. Include also a unique transaction ID at each log line, for more information refer to the bullet below “Write transaction-id to log”. One last point to consider is also including an agent that logs the system resource like memory and CPU like Elastic Beat. -**2. smart aggregation** – once you have comprehensive information on your servers file system, it’s time to periodically push these to a system that aggregates, facilities and visualizes this data. The Elastic stack, for example, is a popular and free choice that offers all the components to aggregate and visualize data. Many commercial products provide similar functionality only they greatly cut down the setup time and require no hosting. +**2. smart aggregation** – once you have comprehensive information on your server's file system, it’s time to periodically push these to a system that aggregates, facilitates and visualizes this data. The Elastic stack, for example, is a popular and free choice that offers all the components to aggregate and visualize data. Many commercial products provide similar functionality only they greatly cut down the setup time and require no hosting. -**3. smart visualization** – now the information is aggregated and searchable, one can be satisfied only with the power of easily searching the logs but this can go much further without coding or spending much effort. We can now show important operational metrics like error rate, average CPU throughout the day, how many new users opted-in in the last hour and any other metric that helps to govern and improve our app +**3. smart visualization** – now the information is aggregated and searchable, one can be satisfied only with the power of easily searching the logs but this can go much further without coding or spending much effort. We can now show important operational metrics like error rate, average CPU throughout the day, how many new users opted-in in the last hour and any other metric that helps to govern and improve our app.

diff --git a/sections/security/commonsecuritybestpractices.md b/sections/security/commonsecuritybestpractices.md index 2280af5d5..7bf016c42 100644 --- a/sections/security/commonsecuritybestpractices.md +++ b/sections/security/commonsecuritybestpractices.md @@ -75,7 +75,7 @@ Going on, below we've listed some important bits of advice from the OWASP projec ## ![✔] OWASP A9: Using Components With Known Security Vulneraibilities -- Scan docker images for known vulnerabilities (using Docker's and other vendors offer scanning services) +- Scan docker images for known vulnerabilities (using Docker's and other vendors' scanning services) - Enable automatic instance (machine) patching and upgrades to avoid running old OS versions that lack security patches - Provide the user with both 'id', 'access' and 'refresh' token so the access token is short-lived and renewed with the refresh token - Log and audit each API call to cloud and management services (e.g who deleted the S3 bucket?) using services like AWS CloudTrail diff --git a/sections/security/dependencysecurity.md b/sections/security/dependencysecurity.md index 38abb514b..33d52ad93 100644 --- a/sections/security/dependencysecurity.md +++ b/sections/security/dependencysecurity.md @@ -37,7 +37,7 @@ An example of the output of the Synk GitHub integration automatically created pu ### Greenkeeper -Greenkeeper is a service which offers real-time dependency updates, which keeps an application more secure by always using the most update to date and patched dependency versions. +Greenkeeper is a service which offers real-time dependency updates, which keeps an application more secure by always using the most up to date and patched dependency versions. Greenkeeper watches the npm dependencies specified in a repository's `package.json` file, and automatically creates a working branch with each dependency update. The repository CI suite is then run to reveal any breaking changes for the updated dependency version in the application. If CI fails due to the dependency update, a clear and concise issue is created in the repository to be auctioned, outlining the current and updated package versions, along with information and commit history of the updated version. diff --git a/sections/security/expirejwt.md b/sections/security/expirejwt.md index 23611945c..ab00278fa 100644 --- a/sections/security/expirejwt.md +++ b/sections/security/expirejwt.md @@ -7,7 +7,7 @@ Due to this, when using JWT authentication, an application should manage a black ### `express-jwt-blacklist` example -An example of running `express-jwt-blacklist` on a Node.js project using the `express-jwt`. Note that it is important to not use the default store settings(in-memory) cache of `express-jwt-blacklist`, but to use an external store such as Redis to revoke tokens across many Node.js processes. +An example of running `express-jwt-blacklist` on a Node.js project using the `express-jwt`. Note that it is important to not use the default store settings (in-memory) cache of `express-jwt-blacklist`, but to use an external store such as Redis to revoke tokens across many Node.js processes. ```javascript const jwt = require('express-jwt'); @@ -26,12 +26,12 @@ blacklist.configure({ } } }); - + app.use(jwt({ secret: 'my-secret', isRevoked: blacklist.isRevoked })); - + app.get('/logout', (req, res) => { blacklist.revoke(req.user) res.sendStatus(200); diff --git a/sections/security/validation.md b/sections/security/validation.md index e37cdd370..35bd692f2 100644 --- a/sections/security/validation.md +++ b/sections/security/validation.md @@ -2,7 +2,7 @@ ### One Paragraph Explainer -Validation is about being very explicit on what payload our app is willing to accept and failing fast should the input deviates from the expectations. This minimizes an attackers surface who can no longer try out payloads with a different structure, values and length. Practically it prevents attacks like DDOS (code is unlikely to fail when the input is well defined) and Insecure Deserialization (JSON contain no surprises). Though validation can be coded or rely upon classes and types (TypeScript, ES6 classes) the community seems to increasingly like JSON-based schemas as these allow declaring complex rules without coding and share the expectations with the frontend. JSON-schema is an emerging standard that is supported by many npm libraries and tools (e.g. [jsonschema](https://www.npmjs.com/package/jsonschema), [Postman](http://blog.getpostman.com/2017/07/28/api-testing-tips-from-a-postman-professional/)), [joi](https://www.npmjs.com/package/joi) is also highly popular with sweet syntax. Typically JSON syntax can't cover all validation scenario and custom code or pre-baked validation frameworks like [validator.js](https://github.com/chriso/validator.js/) come in handy. Regardless of the chosen syntax, ensure to run the validation as early as possible - For example, by using Express middleware that validates the request body before the request is passed to the route handler +Validation is about being very explicit on what payload our app is willing to accept and failing fast should the input deviate from the expectations. This minimizes the attacker's surface who can no longer try out payloads with a different structure, values and length. Practically it prevents attacks like DDOS (code is unlikely to fail when the input is well defined) and Insecure Deserialization (JSON contain no surprises). Though validation can be coded or relied upon classes and types (TypeScript, ES6 classes) the community seems to increasingly like JSON-based schemas as these allow declaring complex rules without coding and share the expectations with the frontend. JSON-schema is an emerging standard that is supported by many npm libraries and tools (e.g. [jsonschema](https://www.npmjs.com/package/jsonschema), [Postman](http://blog.getpostman.com/2017/07/28/api-testing-tips-from-a-postman-professional/)), [joi](https://www.npmjs.com/package/@hapi/joi) is also highly popular with sweet syntax. Typically JSON syntax can't cover all validation scenario and custom code or pre-baked validation frameworks like [validator.js](https://github.com/chriso/validator.js/) come in handy. Regardless of the chosen syntax, ensure to run the validation as early as possible - For example, by using Express middleware that validates the request body before the request is passed to the route handler ### Example - JSON-Schema validation rules @@ -33,7 +33,7 @@ Validation is about being very explicit on what payload our app is willing to ac const JSONValidator = require('jsonschema').Validator; class Product { - + validate() { const v = new JSONValidator(); diff --git a/sections/testingandquality/aaa.md b/sections/testingandquality/aaa.md index 8678da7e9..bc357b2c4 100644 --- a/sections/testingandquality/aaa.md +++ b/sections/testingandquality/aaa.md @@ -3,7 +3,7 @@

### One Paragraph Explainer -Our biggest testing challenge is the lack of headspace - we already have the production code keeping us super-busy. For this reason the testing code must stay dead-simple and easy to understand. When reading a test case - it shouldn't feel like reading imperative code (loops, inheritance) rather more like HTML - a declarative experience. To achieve this, keep the AAA convention so the readers mind will parse the test intent effortlessly. There some other similar formats to this pattern, like XUnit 'Setup, Excercise, Verify, Teardown'. These are the three A: +Our biggest testing challenge is the lack of headspace - we already have the production code keeping us super-busy. For this reason the testing code must stay dead-simple and easy to understand. When reading a test case - it shouldn't feel like reading imperative code (loops, inheritance) rather more like HTML - a declarative experience. To achieve this, keep the AAA convention so the readers' mind will parse the test intent effortlessly. There are some other similar formats to this pattern, like XUnit 'Setup, Excercise, Verify, Teardown'. These are the three A: The 1st A - Arrange: All the setup code to bring the system to the scenario the test aims to simulate. This might include instantiating the unit under test constructor, adding DB records, mocking/stubbing on objects and any other preparation code From 3ecdab034c976c7c96a317bf9ba816c6561032a9 Mon Sep 17 00:00:00 2001 From: Serge Date: Wed, 22 Apr 2020 23:55:34 +0200 Subject: [PATCH 0278/1795] Update README.russian.md Update new best practices href --- README.russian.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.russian.md b/README.russian.md index fdc3d018d..e75142704 100644 --- a/README.russian.md +++ b/README.russian.md @@ -26,7 +26,7 @@ # Последние лучшие практики и новости -- **✅ Новая лучшая практика:** 7.1: [Не блокируйте цикл событий](#7-draft-performance-best-practices) by Keith Holliday +- **✅ Новая лучшая практика:** 7.1: [Не блокируйте цикл событий](#-71-не-блокируйте-цикл-событий) by Keith Holliday - **🇷🇺 Перевод на русский:** Alex Ivanov недавно опубликовал [Russian translation](/README.russian.md) From 26267ba3e5be5a031fa921ffb37d17569ab7980d Mon Sep 17 00:00:00 2001 From: Kirill Shekhovtsov Date: Sat, 25 Apr 2020 03:36:20 +0300 Subject: [PATCH 0279/1795] Fix Russian Locale: Remove tautology, remove excess symbol in 4.3 --- README.russian.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.russian.md b/README.russian.md index fdc3d018d..5f61bf405 100644 --- a/README.russian.md +++ b/README.russian.md @@ -98,9 +98,9 @@

-## ![✔] 1.5 Используйте конфигурацию с учетом среды, безопасную и иерархическую конфигурацию +## ![✔] 1.5 Используйте безопасную и иерархическую конфигурацию с учетом среды -**TL;DR:** Идеальная и безупречная конфигурация конфигурации должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) +**TL;DR:** Идеальная и безупречная конфигурация должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) **Иначе:** Невыполнение каких-либо требований к конфигурации приведет к срывам в работе разработчиков или devops командой. Вероятно, и тех и других. @@ -452,7 +452,7 @@ null == undefined // true

-## ![✔] 4.3 Структурные тесты AAA-подходом] +## ![✔] 4.3 Структурные тесты AAA-подходом **TL;DR:** Структурируйте свои тесты с тремя хорошо разделенными секциями: Arrange, Act & Assert AAA). Первая часть включает в себя настройку теста, затем выполнение тестируемого модуля и, наконец, этап подтверждения. Следование этой структуре гарантирует, что читатель не тратит мозговые ЦП на понимание плана тестирования. From cab232fde2b2de06f06d4800385891552d07595d Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Sat, 25 Apr 2020 23:07:39 +0300 Subject: [PATCH 0280/1795] Update operations-manual.md --- .operations/operations-manual.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.operations/operations-manual.md b/.operations/operations-manual.md index a98efa50c..809ebf2ef 100644 --- a/.operations/operations-manual.md +++ b/.operations/operations-manual.md @@ -85,5 +85,6 @@ Each month, a maintainer on call will open an issue for maintenance work and rec | Korean | Kyle | | Egyptian | Yoni | | Ukrainian | Bruno | -| Polish. | Kevyn | +| Polish | Kevyn | +| Thai | Kevyn | From 2da0e8ef8ce50aec5d50d5b22469f9c5935e8853 Mon Sep 17 00:00:00 2001 From: biesiadamich <60202305+biesiadamich@users.noreply.github.com> Date: Thu, 7 May 2020 11:17:00 +0200 Subject: [PATCH 0281/1795] update readme added Polish flag at the top of the homepage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2a62e3244..e4757356a 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@
-Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) +Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md), [![PL](/assets/flags/PL.png)**PL**](/README.polish.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations)
From 0af142ab79bd8d6e6fc1a1d5a341a4f8e880dc86 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 10 May 2020 08:37:13 +0000 Subject: [PATCH 0282/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e4757356a..b49e7acb9 100644 --- a/README.md +++ b/README.md @@ -1296,6 +1296,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
Kim Kern

🖋
Kenneth Freitas

🖋
songe

🖋 +
Kirill Shekhovtsov

🖋 From 4773de5adb003d24791b23b5904d9c47b237ff78 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 10 May 2020 08:37:14 +0000 Subject: [PATCH 0283/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 182bb4d21..190c85165 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -878,6 +878,15 @@ "contributions": [ "content" ] + }, + { + "login": "Ksedline", + "name": "Kirill Shekhovtsov", + "avatar_url": "https://avatars1.githubusercontent.com/u/30693707?v=4", + "profile": "http://ksed.dev", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From a1171208759b145d39574f0446618d60667360cc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 10 May 2020 10:25:33 +0000 Subject: [PATCH 0284/1795] docs: update README.md [skip ci] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e2a7347aa..1bb43f63f 100644 --- a/README.md +++ b/README.md @@ -1298,6 +1298,9 @@ Thanks goes to these wonderful people who have contributed to this repository!
songe

🖋
Kirill Shekhovtsov

🖋 + +
Serge

🖋 + From 7a0dcac0dc0343f82795ae43846ef24988fa2dbf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 10 May 2020 10:25:34 +0000 Subject: [PATCH 0285/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 190c85165..9f6d485ed 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -887,6 +887,15 @@ "contributions": [ "content" ] + }, + { + "login": "SerzN1", + "name": "Serge", + "avatar_url": "https://avatars0.githubusercontent.com/u/2534649?v=4", + "profile": "https://github.com/SerzN1", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From c860c9a8d68dd84bceedc1e16c89b2eaec6e0091 Mon Sep 17 00:00:00 2001 From: keyrwinz Date: Tue, 12 May 2020 15:01:12 +0800 Subject: [PATCH 0286/1795] added require for the testing package --- sections/projectstructre/separateexpress.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sections/projectstructre/separateexpress.md b/sections/projectstructre/separateexpress.md index 679b2a8be..992f8a3f5 100644 --- a/sections/projectstructre/separateexpress.md +++ b/sections/projectstructre/separateexpress.md @@ -57,6 +57,7 @@ const server = http.createServer(app); Javascript ```javascript +const request = require('supertest'); const app = express(); app.get('/user', (req, res) => { @@ -79,6 +80,7 @@ request(app) Typescript ```typescript +const request = require('supertest'); const app = express(); app.get('/user', (req: Request, res: Response) => { @@ -95,4 +97,4 @@ request(app) }); ``` - \ No newline at end of file + From 8ea1e39e66a1e994cca7323ea12b579dca7e9ad9 Mon Sep 17 00:00:00 2001 From: VinayaSathyanarayana Date: Thu, 14 May 2020 12:00:27 +0530 Subject: [PATCH 0287/1795] Update commonsecuritybestpractices.md --- .../security/commonsecuritybestpractices.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/sections/security/commonsecuritybestpractices.md b/sections/security/commonsecuritybestpractices.md index 7bf016c42..0e2e6f558 100644 --- a/sections/security/commonsecuritybestpractices.md +++ b/sections/security/commonsecuritybestpractices.md @@ -108,4 +108,24 @@ Going on, below we've listed some important bits of advice from the OWASP projec - India: https://meity.gov.in/writereaddata/files/Personal_Data_Protection_Bill,2018.pdf - Singapore: https://www.pdpc.gov.sg/Legislation-and-Guidelines/Personal-Data-Protection-Act-Overview +## ![✔] Have a security.txt File [PRODUCTION] + +**TL;DR:** Have a text file called ```security.txt``` under ```/.well-known``` directory (/.well-known/security.txt) or in the root directory (/security.txt) of your website or your web application in production. ```security.txt``` file should contain details using which security researchers can report vulnerabilities and also the contact details of the responsible person/group (email id and/or phone numbers) to whom the reports have to be sent. + +**Otherwise:** You may not be notified about the vulnerabilities. You will miss the opportunity to act on the vulnerabilities in time. + +🔗 [**Read More: security.txt**](https://securitytxt.org/) +


+ +## ![✔] Have a SECURITY.md File [OPEN SOURCE] + +**TL;DR:** To give people instructions for responsibly reporting security vulnerabilities in your project, you can add a SECURITY.md file to your repository's root, docs, or .github folder. SECURITY.md file should contain details using which security researchers can report vulnerabilities and also the contact details of the responsible person/group (email id and/or phone numbers) to whom the reports have to be sent. + +**Otherwise:** You may not be notified about the vulnerabilities. You will miss the opportunity to act on the vulnerabilities in time. + +🔗 [**Read More: SECURITY.md**](https://help.github.com/en/github/managing-security-vulnerabilities/adding-a-security-policy-to-your-repository) + +


+ +


From 227e00d62800509abd832e36ece3c02e815dea90 Mon Sep 17 00:00:00 2001 From: keyrwinz Date: Sat, 16 May 2020 21:54:42 +0800 Subject: [PATCH 0288/1795] use import in typescript instead of require --- sections/projectstructre/separateexpress.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/separateexpress.md b/sections/projectstructre/separateexpress.md index 992f8a3f5..e35c67d24 100644 --- a/sections/projectstructre/separateexpress.md +++ b/sections/projectstructre/separateexpress.md @@ -80,7 +80,7 @@ request(app) Typescript ```typescript -const request = require('supertest'); +import * as request from "supertest"; const app = express(); app.get('/user', (req: Request, res: Response) => { From 8b73dd3c9945107929d51bbb249638bd76b453fc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 09:51:18 +0000 Subject: [PATCH 0289/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bb43f63f..d1a8eb30b 100644 --- a/README.md +++ b/README.md @@ -1300,6 +1300,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
Serge

🖋 +
keyrwinz

🖋 From 602ddae8859c5050655ff73ae4d5af38101b581e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 09:51:19 +0000 Subject: [PATCH 0290/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 9f6d485ed..af3e8352a 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -896,6 +896,15 @@ "contributions": [ "content" ] + }, + { + "login": "keyrwinz", + "name": "keyrwinz", + "avatar_url": "https://avatars3.githubusercontent.com/u/21241761?v=4", + "profile": "https://github.com/keyrwinz", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From cf9daf875f8f8701b793c7967e0ff0d54fb4fba5 Mon Sep 17 00:00:00 2001 From: Dmitry Nikitenko Date: Sun, 17 May 2020 16:15:55 +0600 Subject: [PATCH 0291/1795] fix a misspelling of package-lock.json filename --- sections/production/lockdependencies.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/production/lockdependencies.md b/sections/production/lockdependencies.md index 4bacd048a..1698c9bbb 100644 --- a/sections/production/lockdependencies.md +++ b/sections/production/lockdependencies.md @@ -39,7 +39,7 @@ save-exact:true

-### Code example: npm 5 dependencies lock file – package.json +### Code example: npm 5 dependencies lock file – package-lock.json ```json { From 6d220f49de0b9f00f4a9cd0a7f7c7211490e6726 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 10:35:02 +0000 Subject: [PATCH 0292/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d1a8eb30b..0727fc9e6 100644 --- a/README.md +++ b/README.md @@ -1301,6 +1301,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
Serge

🖋
keyrwinz

🖋 +
Dmitry Nikitenko

🖋 From 91a99be148a82087d774176b0cac39cf9e9e7ff6 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 17 May 2020 10:35:03 +0000 Subject: [PATCH 0293/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index af3e8352a..89a3acf16 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -905,6 +905,15 @@ "contributions": [ "content" ] + }, + { + "login": "nDmitry", + "name": "Dmitry Nikitenko", + "avatar_url": "https://avatars0.githubusercontent.com/u/2134568?v=4", + "profile": "https://github.com/nDmitry", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 165ccbed3b7c4f59386232fb106737a4c0ce959b Mon Sep 17 00:00:00 2001 From: blackmatch Date: Mon, 1 Jun 2020 17:47:10 +0800 Subject: [PATCH 0294/1795] Translate 6.24 to Chinese --- README.chinese.md | 12 +++++ sections/security/saferedirects.chinese.md | 55 ++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 sections/security/saferedirects.chinese.md diff --git a/README.chinese.md b/README.chinese.md index 9f12c6566..7c5880cdd 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -926,6 +926,18 @@ null == undefined // true


+## ![✔] 6.24. 避免不安全的重定向 + + + +**TL;DR:** 不验证用户输入的重定向可使攻击者启动网络钓鱼诈骗,窃取用户凭据,以及执行其他恶意操作。 + +**否则:** 当攻击者发现你没有校验用户提供的外部输入时,他们会在论坛、社交媒体以和其他公共场合发布他们精心制作的链接来诱使用户点击,以此达到漏洞利用的目的。 + +🔗 [**阅读更多: 避免不安全的重定向**](/sections/security/saferedirects.chinese.md) + +

+

⬆ Return to top

# `API Practices` diff --git a/sections/security/saferedirects.chinese.md b/sections/security/saferedirects.chinese.md new file mode 100644 index 000000000..4a756894f --- /dev/null +++ b/sections/security/saferedirects.chinese.md @@ -0,0 +1,55 @@ +# 避免不安全的重定向 + +### 一段解释 + +当我们在Node.js或者Express中实现重定向时,在服务器端进行输入校验非常重要。当攻击者发现你没有校验用户提供的外部输入时,他们会在论坛、社交媒体以和其他公共场合发布他们精心制作的链接来诱使用户点击,以此达到漏洞利用的目的。 + +案例:express使用用户输入的不安全的重定向 +```javascript +const express = require('express'); +const app = express(); + +app.get('/login', (req, res, next) => { + + if (req.session.isAuthenticated()) { + res.redirect(req.query.url); + } + +}); +``` + +建议的避免不安全重定向的方案是,避免依赖用户输入的内容来进行重定向。如果一定要使用用户输入的内容,可以通过使用白名单重定向的方式来避免暴露漏洞。 + +案例:使用白名单实现安全的重定向 +```javascript +const whitelist = { + 'https://google.com': 1 +}; + +function getValidRedirect(url) { + // 检查url是否以/开头 + if (url.match(/^\/(?!\/)/)) { + // 前置我们的域名来确保(安全) + return 'https://example.com' + url; + } + + // 否则对照白名单列表 + return whitelist[url] ? url : '/'; +} + +app.get('/login', (req, res, next) => { + + if (req.session.isAuthenticated()) { + res.redirect(getValidRedirect(req.query.url)); + } + +}); +``` + +### 其他博主的看法 + +来自博客[NodeSwat](https://blog.nodeswat.com/unvalidated-redirects-b0a2885720db): +> 幸运的是,缓解此漏洞的方法非常简单-不要使用未经验证的用户输入作为重定向的基础。 + +来自博客[Hailstone](https://blog.hailstone.io/how-to-prevent-unsafe-redirects-in-node-js/): +> 但是,如果服务器端的重定向逻辑没有对url参数的数据进行校验的话,则你的用户可能最终访问的地址跟你的地址看起来几乎完全一致(examp1e.com),但这最终满足了犯罪黑客们的需求。 From 7343a1a3258f147fd908535ec18db2820a8cc506 Mon Sep 17 00:00:00 2001 From: blackmatch Date: Mon, 1 Jun 2020 17:52:31 +0800 Subject: [PATCH 0295/1795] fix some writing format --- README.chinese.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.chinese.md b/README.chinese.md index 7c5880cdd..b7c5819e5 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -32,7 +32,7 @@ 3. [编码规范实践 (12) ](#3-code-style-practices) 4. [测试和总体质量实践 (8) ](#4-testing-and-overall-quality-practices) 5. [进入生产实践 (16) ](#5-going-to-production-practices) -6. :star: 新: [安全实践(23)](#6-security-best-practices) +6. :star: 新: [安全实践(24)](#6-security-best-practices) 7. Performance Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) @@ -924,7 +924,7 @@ null == undefined // true **否则:** 这只是一个起到教育意义的假设: 给定许多Node.js应用程序, 如果我们尝试传递一个空的JSON正文到所有POST请求 - 少数应用程序将崩溃。在这一点上, 我们可以只是重复发送相同的请求, 就可以轻松地搞垮应用程序。 -


+

## ![✔] 6.24. 避免不安全的重定向 From c178ddbc3f6673effe8eec5f4e9d2a0dded7f218 Mon Sep 17 00:00:00 2001 From: blackmatch Date: Mon, 1 Jun 2020 19:14:02 +0800 Subject: [PATCH 0296/1795] fix return to top for README.chinese.md --- README.chinese.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.chinese.md b/README.chinese.md index b7c5819e5..2476354c4 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -26,7 +26,7 @@


-## [目录](#table-of-contents) +## 目录 1. [项目结构实践 (5) ](#1-project-structure-practices) 2. [异常处理实践 (11) ](#2-error-handling-practices) 3. [编码规范实践 (12) ](#3-code-style-practices) @@ -90,7 +90,7 @@


-

⬆ 返回顶部

+

⬆ 返回顶部

2. 错误处理最佳实践

@@ -214,7 +214,7 @@


-

⬆ 返回顶部

+

⬆ 返回顶部

3. 编码风格实践

@@ -386,7 +386,7 @@ null == undefined // true


-

⬆ 返回顶部

+

⬆ 返回顶部

4. 测试和总体的质量实践

@@ -462,7 +462,7 @@ null == undefined // true


-

⬆ 返回顶部

+

⬆ 返回顶部

5. 上线实践

@@ -650,7 +650,7 @@ null == undefined // true


-

⬆ 返回顶部

+

⬆ 返回顶部

6. 安全最佳实践

@@ -938,7 +938,7 @@ null == undefined // true

-

⬆ Return to top

+

⬆ 返回顶部

# `API Practices` From 6e92a87b3649cd88cc086d99d2f71580cbfe8022 Mon Sep 17 00:00:00 2001 From: bushuai Date: Tue, 2 Jun 2020 16:53:14 +0800 Subject: [PATCH 0297/1795] fix: typo --- README.chinese.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.chinese.md b/README.chinese.md index 9f12c6566..a02a69944 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -770,7 +770,7 @@ null == undefined // true **否则:** 您疏忽和宽松的方法大大增加了攻击面, 并鼓励攻击者尝试许多输入, 直到他们找到一些组合, 使应用程序崩溃。 -🔗 [**更多: 验证传人的JSON schemas**](/sections/security/validation.md) +🔗 [**更多: 验证传入的JSON schemas**](/sections/security/validation.md)

From b9b53976be5e6956764d5cab340f46c3283cb04f Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Thu, 4 Jun 2020 13:32:44 +0300 Subject: [PATCH 0298/1795] Remove unmaintained loggers and recommend pino first --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0727fc9e6..c01a6493b 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines ## ![✔] 2.7 Use a mature logger to increase error visibility -**TL;DR:** A set of mature logging tools like [Winston](https://www.npmjs.com/package/winston), [Bunyan](https://github.com/trentm/node-bunyan), [Log4js](http://stritti.github.io/log4js/) or [Pino](https://github.com/pinojs/pino), will speed-up error discovery and understanding. So forget about console.log +**TL;DR:** A set of mature logging tools like [Pino](https://github.com/pinojs/pino) or [Log4js](https://www.npmjs.com/package/log4js), will speed-up error discovery and understanding. So forget about console.log **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late From fa94eb17139ae43282333f7bc8e6122e7ae3e83b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:21:22 +0000 Subject: [PATCH 0299/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0727fc9e6..c3810d8fc 100644 --- a/README.md +++ b/README.md @@ -1302,6 +1302,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
Serge

🖋
keyrwinz

🖋
Dmitry Nikitenko

🖋 +
bushuai

👀 🖋 From af7153e7d6eb829ab6d9aa4030896d1bc262e6d4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2020 07:21:23 +0000 Subject: [PATCH 0300/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 89a3acf16..124b7f67b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -914,6 +914,16 @@ "contributions": [ "content" ] + }, + { + "login": "bushuai", + "name": "bushuai", + "avatar_url": "https://avatars0.githubusercontent.com/u/1875256?v=4", + "profile": "https://bushuai.cc", + "contributions": [ + "review", + "content" + ] } ], "projectName": "nodebestpractices", From c5945a76447af16ef76df1f85fe26ff94e924331 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Fri, 5 Jun 2020 12:06:56 +0200 Subject: [PATCH 0301/1795] Replace i0natan with goldbergyoni in links --- .operations/operations-manual.md | 2 +- .operations/package.json | 6 ++--- .operations/writing-guidelines.chinese.md | 2 +- .operations/writing-guidelines.md | 2 +- .operations/writing-guidelines.polish.md | 2 +- .operations/writing-guidelines.russian.md | 2 +- README.brazilian-portuguese.md | 24 +++++++++---------- README.chinese.md | 8 +++---- README.korean.md | 18 +++++++------- README.md | 18 +++++++------- README.polish.md | 18 +++++++------- README.russian.md | 18 +++++++------- .../apmproducts.brazilian-portuguese.md | 4 ++-- sections/errorhandling/apmproducts.chinese.md | 4 ++-- sections/errorhandling/apmproducts.korean.md | 4 ++-- sections/errorhandling/apmproducts.md | 4 ++-- sections/errorhandling/apmproducts.polish.md | 4 ++-- sections/errorhandling/apmproducts.russian.md | 4 ++-- ...entingusingswagger.brazilian-portuguese.md | 2 +- .../documentingusingswagger.chinese.md | 2 +- .../documentingusingswagger.korean.md | 2 +- .../errorhandling/documentingusingswagger.md | 2 +- .../documentingusingswagger.polish.md | 2 +- .../documentingusingswagger.russian.md | 2 +- ...breakintcomponents.brazilian-portuguese.md | 4 ++-- .../breakintcomponents.chinese.md | 4 ++-- .../breakintcomponents.japanese.md | 4 ++-- .../breakintcomponents.korean.md | 4 ++-- .../projectstructre/breakintcomponents.md | 4 ++-- .../breakintcomponents.polish.md | 4 ++-- .../breakintcomponents.russian.md | 4 ++-- .../createlayers.brazilian-portuguese.md | 4 ++-- .../projectstructre/createlayers.chinese.md | 4 ++-- .../projectstructre/createlayers.korean.md | 4 ++-- sections/projectstructre/createlayers.md | 4 ++-- .../projectstructre/createlayers.polish.md | 4 ++-- .../projectstructre/createlayers.russian.md | 4 ++-- .../projectstructre/thincomponents.chinese.md | 4 ++-- sections/projectstructre/thincomponents.md | 4 ++-- .../projectstructre/thincomponents.russian.md | 4 ++-- .../wraputilities.brazilian-portuguese.md | 2 +- .../projectstructre/wraputilities.chinese.md | 2 +- .../projectstructre/wraputilities.korean.md | 2 +- sections/projectstructre/wraputilities.md | 2 +- .../projectstructre/wraputilities.polish.md | 2 +- .../projectstructre/wraputilities.russian.md | 2 +- sections/template.md | 6 ++--- .../3-parts-in-name.brazilian-portuguese.md | 2 +- sections/testingandquality/3-parts-in-name.md | 2 +- .../3-parts-in-name.polish.md | 2 +- .../3-parts-in-name.russian.md | 2 +- sections/testingandquality/bumpversion.md | 2 +- .../citools.brazilian-portuguese.md | 4 ++-- sections/testingandquality/citools.chinese.md | 4 ++-- sections/testingandquality/citools.korean.md | 4 ++-- sections/testingandquality/citools.md | 4 ++-- sections/testingandquality/citools.polish.md | 4 ++-- sections/testingandquality/citools.russian.md | 4 ++-- .../refactoring.brazilian-portuguese.md | 6 ++--- sections/testingandquality/refactoring.md | 6 ++--- .../testingandquality/refactoring.polish.md | 6 ++--- .../testingandquality/refactoring.russian.md | 6 ++--- 62 files changed, 148 insertions(+), 148 deletions(-) diff --git a/.operations/operations-manual.md b/.operations/operations-manual.md index 809ebf2ef..f7b3fcb92 100644 --- a/.operations/operations-manual.md +++ b/.operations/operations-manual.md @@ -8,7 +8,7 @@ In a nutshell, every issue and PR should get tagged by one of our core team and There is no specific person on call who assigns inquiries rather we count on our core team to visit almost everyday and assign issues/PR - this way, the workflow is not depend upon any specific person rather on our entire team. -Any new content should conform to our [writing guidelines](https://github.com/i0natan/nodebestpractices/blob/master/.operations/writing-guidelines.md) +Any new content should conform to our [writing guidelines](https://github.com/goldbergyoni/nodebestpractices/blob/master/.operations/writing-guidelines.md) ## Monthly maintenance diff --git a/.operations/package.json b/.operations/package.json index 8da9d7a58..f4629e887 100644 --- a/.operations/package.json +++ b/.operations/package.json @@ -10,14 +10,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/i0natan/nodebestpractices.git" + "url": "git+https://github.com/goldbergyoni/nodebestpractices.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/i0natan/nodebestpractices/issues" + "url": "https://github.com/goldbergyoni/nodebestpractices/issues" }, - "homepage": "https://github.com/i0natan/nodebestpractices#readme", + "homepage": "https://github.com/goldbergyoni/nodebestpractices#readme", "dependencies": { "cheerio": "^1.0.0-rc.2", "github-api": "^3.0.0", diff --git a/.operations/writing-guidelines.chinese.md b/.operations/writing-guidelines.chinese.md index 8d52d3549..f799ce2d4 100644 --- a/.operations/writing-guidelines.chinese.md +++ b/.operations/writing-guidelines.chinese.md @@ -18,7 +18,7 @@ 除了大量编辑和可靠的内容, 通过它略读也应该提供全面覆盖的主题。不应排除重要的子主题 ## 4. 一致的格式 -内容是使用固定模板显示的。任何将来的内容都必须符合同一模板。如果希望添加新项目符号, 请从现有项目符号复制项目符号格式, 并将其扩展以满足您的需要。有关其他信息, 请查看[模版](https://github.com/i0natan/nodebestpractices/blob/master/sections/template.md) +内容是使用固定模板显示的。任何将来的内容都必须符合同一模板。如果希望添加新项目符号, 请从现有项目符号复制项目符号格式, 并将其扩展以满足您的需要。有关其他信息, 请查看[模版](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md) ## 5. 关于Node.js 每个建议都应直接与Node.js相关, 而不是一般软件开发。当我们建议在Node.js中实现通用模式/规则时, 内容应该集中在Node的实现上。例如, 当我们建议将所有请求的输入为了安全原因进行处理时, 应使用Node行话 - '使用中间件来处理请求输入' diff --git a/.operations/writing-guidelines.md b/.operations/writing-guidelines.md index c0e6d6ea8..155b6bafa 100644 --- a/.operations/writing-guidelines.md +++ b/.operations/writing-guidelines.md @@ -16,7 +16,7 @@ Apart from the content being greatly edited and reliable, skimming through it sh ## 4. Consistent formatting -The content is presented using fixed templates. Any future content must conform to the same template. If you wish to add new bullets copy a bullet format from an existing bullet and extend it to your needs. For additional information please view [this template](https://github.com/i0natan/nodebestpractices/blob/master/sections/template.md). +The content is presented using fixed templates. Any future content must conform to the same template. If you wish to add new bullets copy a bullet format from an existing bullet and extend it to your needs. For additional information please view [this template](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md). ## 5. It's About Node.js diff --git a/.operations/writing-guidelines.polish.md b/.operations/writing-guidelines.polish.md index 43de8cc26..471525ade 100644 --- a/.operations/writing-guidelines.polish.md +++ b/.operations/writing-guidelines.polish.md @@ -16,7 +16,7 @@ Oprócz tego, że treść jest znacznie edytowana i niezawodna, przeglądanie w ## 4. Spójne formatowanie -Treść jest prezentowana przy użyciu stałych szablonów. Wszelkie przyszłe treści muszą być zgodne z tym samym szablonem. Jeśli chcesz dodać nowe punktory, skopiuj format punktora z istniejącego i rozszerz go do swoich potrzeb. Aby uzyskać dodatkowe informacje, zobacz [ten szablon](https://github.com/i0natan/nodebestpractices/blob/master/sections/template.md). +Treść jest prezentowana przy użyciu stałych szablonów. Wszelkie przyszłe treści muszą być zgodne z tym samym szablonem. Jeśli chcesz dodać nowe punktory, skopiuj format punktora z istniejącego i rozszerz go do swoich potrzeb. Aby uzyskać dodatkowe informacje, zobacz [ten szablon](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md). ## 5. To na temat Node.js diff --git a/.operations/writing-guidelines.russian.md b/.operations/writing-guidelines.russian.md index c1bff4332..0ef1389cf 100644 --- a/.operations/writing-guidelines.russian.md +++ b/.operations/writing-guidelines.russian.md @@ -16,7 +16,7 @@ ## 4. Согласованное форматирование -Контент представлен с использованием фиксированных шаблонов. Любое будущее содержание должно соответствовать тому же шаблону. Если вы хотите добавить новые маркеры, скопируйте формат маркера из существующего маркера и расширьте его для своих нужд. Для получения дополнительной информации, пожалуйста, просмотрите [этот шаблон](https://github.com/i0natan/nodebestpractices/blob/master/sections/template.md). +Контент представлен с использованием фиксированных шаблонов. Любое будущее содержание должно соответствовать тому же шаблону. Если вы хотите добавить новые маркеры, скопируйте формат маркера из существующего маркера и расширьте его для своих нужд. Для получения дополнительной информации, пожалуйста, просмотрите [этот шаблон](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md). ## 5. Это про Node.js diff --git a/README.brazilian-portuguese.md b/README.brazilian-portuguese.md index 3d2b3f48a..e98bb87b8 100644 --- a/README.brazilian-portuguese.md +++ b/README.brazilian-portuguese.md @@ -38,7 +38,7 @@ Leia em diferentes idiomas: [![CN](/assets/flags/CN.png)**CN**](/README.chinese. **1. Quando você lê aqui, na verdade você lê alguns dos melhores artigos de Node.js -** este é um resumo e curadoria dos mais bem ranqueados conteúdos sobre as melhores práticas do Node.js. -**2. Esta é a maior coletânea, e está crescendo mais a cada semana -** atualmente, são apresentadas mais de 80 melhores práticas, guias de estilo e dicas de arquitetura. Novas issues e PR são criadas diariamente para manter este livro vivo atualizado. Gostaríamos muito de ver você contribuindo aqui, seja corrigindo algum erro de código ou sugerindo novas e brilhantes ideias. Veja nossas [conquistas aqui](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open). +**2. Esta é a maior coletânea, e está crescendo mais a cada semana -** atualmente, são apresentadas mais de 80 melhores práticas, guias de estilo e dicas de arquitetura. Novas issues e PR são criadas diariamente para manter este livro vivo atualizado. Gostaríamos muito de ver você contribuindo aqui, seja corrigindo algum erro de código ou sugerindo novas e brilhantes ideias. Veja nossas [conquistas aqui](https://github.com/goldbergyoni/nodebestpractices/milestones?direction=asc&sort=due_date&state=open). **3. A maioria dos tópicos possuem informações adicionais -** perto dos tópicos das melhores práticas, você encontrará o link **🔗Leia Mais** que irá apresentar exemplos de códigos, citações de blogs selecionados e mais informações. @@ -455,7 +455,7 @@ Todas as declarações acima false se feitas com `===`. ## ![✔] 4.3 Detecte problemas de código com um linter -**TL;DR:** Use um code linter para checar a qualidade básica e detectar antipadrões antecipadamente. Rode-o antes de qualquer teste e adicione-o como um pre-commit git-hook para minimizar o tempo necessário para revisar e corrigir qualquer problema. Veja também [Seção 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices) no Prática de Estilo de Código. +**TL;DR:** Use um code linter para checar a qualidade básica e detectar antipadrões antecipadamente. Rode-o antes de qualquer teste e adicione-o como um pre-commit git-hook para minimizar o tempo necessário para revisar e corrigir qualquer problema. Veja também [Seção 3](https://github.com/goldbergyoni/nodebestpractices#3-code-style-practices) no Prática de Estilo de Código. **Caso contrário:** Você pode deixar passar algum antipadrão e possível código vulnerável para seu ambiente de produção. @@ -1019,7 +1019,7 @@ Todas as declarações acima false se feitas com `===`. # `7. Boas Práticas em Performance` -## Nossos colaboradores estão trabalhando nesta seção. [Gostaria de participar?](https://github.com/i0natan/nodebestpractices/issues/256) +## Nossos colaboradores estão trabalhando nesta seção. [Gostaria de participar?](https://github.com/goldbergyoni/nodebestpractices/issues/256) ## ![✔] 7.1. Prefira métodos JS nativos ao invés de utilitários de usuário, como o Lodash @@ -1034,7 +1034,7 @@ Tenha em mente que, com a introdução do novo motor V8 juntamente com os novos # Feitos -Para manter este guia e deixá-lo atualizado, estamos constantemente atualizando e aprimorando as diretrizes e as práticas recomendadas com a ajuda da comunidade. Você pode acompanhar nossos [feitos](https://github.com/i0natan/nodebestpractices/milestones) e se juntar aos grupos de trabalho, caso queira contribuir com este projeto. +Para manter este guia e deixá-lo atualizado, estamos constantemente atualizando e aprimorando as diretrizes e as práticas recomendadas com a ajuda da comunidade. Você pode acompanhar nossos [feitos](https://github.com/goldbergyoni/nodebestpractices/milestones) e se juntar aos grupos de trabalho, caso queira contribuir com este projeto.
@@ -1049,22 +1049,22 @@ Todas as traduções são contribuições da comunidade. Nós ficaremos felizes ### Traduções em andamento -- ![FR](/assets/flags/FR.png) [Francês](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussão](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) Hebraico ([Discussão](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [Coreano](https://github.com/i0natan/nodebestpractices/blob/korean-translation/README.md) ([Discussão](https://github.com/i0natan/nodebestpractices/issues/94)) -- ![RU](/assets/flags/RU.png) [Russo](https://github.com/i0natan/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussão](https://github.com/i0natan/nodebestpractices/issues/454)) -- ![ES](/assets/flags/ES.png) [Espanhol](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussão](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) Turco ([Discussão](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [Francês](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebraico ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Coreano](https://github.com/goldbergyoni/nodebestpractices/blob/korean-translation/README.md) ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![RU](/assets/flags/RU.png) [Russo](https://github.com/goldbergyoni/nodebestpractices/blob/russian-translation/README.russian.md) ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/454)) +- ![ES](/assets/flags/ES.png) [Espanhol](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turco ([Discussão](https://github.com/goldbergyoni/nodebestpractices/issues/139))

## Comitê Diretivo -Conheça os membros do comitê diretivo - as pessoas que trabalham juntas para fornecer orientação e direção futura para o projeto. Além disso, cada membro do comitê lidera um projeto rastreado em nossos [projetos do Github](https://github.com/i0natan/nodebestpractices/projects). +Conheça os membros do comitê diretivo - as pessoas que trabalham juntas para fornecer orientação e direção futura para o projeto. Além disso, cada membro do comitê lidera um projeto rastreado em nossos [projetos do Github](https://github.com/goldbergyoni/nodebestpractices/projects). -[Yoni Goldberg](https://github.com/i0natan) +[Yoni Goldberg](https://github.com/goldbergyoni) diff --git a/README.chinese.md b/README.chinese.md index a02a69944..647b60015 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -20,7 +20,7 @@ # 欢迎! 首先您应该知道的三件事情: **1. 当您读到这里,实际上您读了很多关于Node.js的优秀文章 -** 这是对Node.js最佳实践中排名最高的内容的总结和分享 -**2. 这里是最大的汇集,且每周都在增长 -** 当前,超过50个最佳实现,样式指南,架构建议已经呈现。每天都有新的issue和PR被创建,以使这本在线书籍不断更新。我们很乐于见到您能在这里做出贡献,不管是修复一些代码的错误,或是提出绝妙的新想法。请查看我们的[milestones](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) +**2. 这里是最大的汇集,且每周都在增长 -** 当前,超过50个最佳实现,样式指南,架构建议已经呈现。每天都有新的issue和PR被创建,以使这本在线书籍不断更新。我们很乐于见到您能在这里做出贡献,不管是修复一些代码的错误,或是提出绝妙的新想法。请查看我们的[milestones](https://github.com/goldbergyoni/nodebestpractices/milestones?direction=asc&sort=due_date&state=open) **3. 大部分的条目包含额外的信息 -** 大部分的最佳实践条目的旁边,您将发现 **🔗Read More** 链接,它将呈现给您示例代码,博客引用和更多信息 @@ -33,7 +33,7 @@ 4. [测试和总体质量实践 (8) ](#4-testing-and-overall-quality-practices) 5. [进入生产实践 (16) ](#5-going-to-production-practices) 6. :star: 新: [安全实践(23)](#6-security-best-practices) -7. Performance Practices ([coming soon](https://github.com/i0natan/nodebestpractices/milestones?direction=asc&sort=due_date&state=open)) +7. Performance Practices ([coming soon](https://github.com/goldbergyoni/nodebestpractices/milestones?direction=asc&sort=due_date&state=open))


@@ -401,7 +401,7 @@ null == undefined // true ## ![✔] 4.2 使用一个linter检测代码问题 -**TL;DR:** 使用代码linter检查基本质量并及早检测反模式。在任何测试之前运行它, 并将其添加为预提交的git钩子, 以最小化审查和更正任何问题所需的时间。也可在[Section 3](https://github.com/i0natan/nodebestpractices#3-code-style-practices)中查阅编码样式实践 +**TL;DR:** 使用代码linter检查基本质量并及早检测反模式。在任何测试之前运行它, 并将其添加为预提交的git钩子, 以最小化审查和更正任何问题所需的时间。也可在[Section 3](https://github.com/goldbergyoni/nodebestpractices#3-code-style-practices)中查阅编码样式实践 **否则:** 您可能让一些反模式和易受攻击的代码传递到您的生产环境中。 @@ -939,7 +939,7 @@ null == undefined // true


# Milestones -To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project. +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/goldbergyoni/nodebestpractices/milestones) and join the working groups if you want to contribute to this project.

diff --git a/README.korean.md b/README.korean.md index d4f9d27c5..b4590d18b 100644 --- a/README.korean.md +++ b/README.korean.md @@ -1050,7 +1050,7 @@ null == undefined // true # `7. 초안: 성능` -## 협력자들이 현재 작업중입니다. [함께 하시겠습니까?](https://github.com/i0natan/nodebestpractices/issues/256) +## 협력자들이 현재 작업중입니다. [함께 하시겠습니까?](https://github.com/goldbergyoni/nodebestpractices/issues/256)

@@ -1077,7 +1077,7 @@ null == undefined // true # 마일스톤 -이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. [마일스톤](https://github.com/i0natan/nodebestpractices/milestones)을 확인하시고 이 프로젝트에 기여하고 싶다면 작업중인 그룹에 참여하세요! +이 가이드를 관리하고 최신 버전을 유지하기 위해, 우리는 지속해서 가이드라인과 모범 사례들을 커뮤니티의 도움으로 업데이트하고 개선해 나가고 있습니다. [마일스톤](https://github.com/goldbergyoni/nodebestpractices/milestones)을 확인하시고 이 프로젝트에 기여하고 싶다면 작업중인 그룹에 참여하세요!
@@ -1093,21 +1093,21 @@ null == undefined // true ### 번역 작업중 -- ![FR](/assets/flags/FR.png) [프랑스어](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) 히브리어 ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [한국어](README.korean.md) - [한상범](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) 제공 -- ![ES](/assets/flags/ES.png) [스페인어](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) 터키어 ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [프랑스어](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) 히브리어 ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [한국어](README.korean.md) - [한상범](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) 제공 +- ![ES](/assets/flags/ES.png) [스페인어](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) 터키어 ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139))

## 운영 위원회 -프로젝트를 지도하고 앞으로 나아갈 방향을 제시하는데 함께 일하는 운영 위원회 일원들을 소개합니다. 추가로, 위원회원들은 각자 [Github 프로젝트](https://github.com/i0natan/nodebestpractices/projects) 아래에 인행되는 프로젝트들을 인솔합니다. +프로젝트를 지도하고 앞으로 나아갈 방향을 제시하는데 함께 일하는 운영 위원회 일원들을 소개합니다. 추가로, 위원회원들은 각자 [Github 프로젝트](https://github.com/goldbergyoni/nodebestpractices/projects) 아래에 인행되는 프로젝트들을 인솔합니다. -[요니 골드버그](https://github.com/i0natan) +[요니 골드버그](https://github.com/goldbergyoni) diff --git a/README.md b/README.md index 0727fc9e6..e8f5c9140 100644 --- a/README.md +++ b/README.md @@ -1047,7 +1047,7 @@ All statements above will return false if used with `===` # `7. Draft: Performance Best Practices` -## Our contributors are working on this section. [Would you like to join?](https://github.com/i0natan/nodebestpractices/issues/256) +## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/256)

@@ -1074,7 +1074,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E # Milestones -To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/i0natan/nodebestpractices/milestones) and join the working groups if you want to contribute to this project +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/goldbergyoni/nodebestpractices/milestones) and join the working groups if you want to contribute to this project
@@ -1091,21 +1091,21 @@ All translations are contributed by the community. We will be happy to get any h ### Translations in progress -- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139))

## Steering Committee -Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/i0natan/nodebestpractices/projects). +Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/goldbergyoni/nodebestpractices/projects). -[Yoni Goldberg](https://github.com/i0natan) +[Yoni Goldberg](https://github.com/goldbergyoni) diff --git a/README.polish.md b/README.polish.md index 325649bff..511d56fa0 100644 --- a/README.polish.md +++ b/README.polish.md @@ -1027,7 +1027,7 @@ Wszystkie powyższe instrukcje zwrócą wartość false, jeśli zostaną użyte # `7. Wersja robocza: Najlepsze praktyki dotyczące wydajności` -## Nasi współpracownicy pracują nad tą sekcją. [Chciałbyś dołączyć?](https://github.com/i0natan/nodebestpractices/issues/256) +## Nasi współpracownicy pracują nad tą sekcją. [Chciałbyś dołączyć?](https://github.com/goldbergyoni/nodebestpractices/issues/256)

@@ -1056,7 +1056,7 @@ Wszystkie powyższe instrukcje zwrócą wartość false, jeśli zostaną użyte # Milestones -Aby utrzymać ten przewodnik i aktualizować go, stale aktualizujemy i ulepszamy wytyczne i najlepsze praktyki z pomocą społeczności. Możesz śledzić nasze [kamienie milowe](https://github.com/i0natan/nodebestpractices/milestones) i dołączyć do grup roboczych, jeśli chcesz przyczynić się do tego projektu +Aby utrzymać ten przewodnik i aktualizować go, stale aktualizujemy i ulepszamy wytyczne i najlepsze praktyki z pomocą społeczności. Możesz śledzić nasze [kamienie milowe](https://github.com/goldbergyoni/nodebestpractices/milestones) i dołączyć do grup roboczych, jeśli chcesz przyczynić się do tego projektu
@@ -1073,21 +1073,21 @@ Wszystkie tłumaczenia pochodzą od społeczności. Z przyjemnością uzyskamy w ### Tłumaczenia w trakcie -- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139))

## Steering Committee -Spotkaj się z członkami komitetu sterującego - ludźmi, którzy pracują razem, aby zapewnić wytyczne i przyszłe kierunki projektu. Ponadto każdy członek komitetu prowadzi projekt śledzony w ramach naszych [projektów GitHub](https://github.com/i0natan/nodebestpractices/projects). +Spotkaj się z członkami komitetu sterującego - ludźmi, którzy pracują razem, aby zapewnić wytyczne i przyszłe kierunki projektu. Ponadto każdy członek komitetu prowadzi projekt śledzony w ramach naszych [projektów GitHub](https://github.com/goldbergyoni/nodebestpractices/projects). -[Yoni Goldberg](https://github.com/i0natan) +[Yoni Goldberg](https://github.com/goldbergyoni) diff --git a/README.russian.md b/README.russian.md index fd1315311..e332b40ab 100644 --- a/README.russian.md +++ b/README.russian.md @@ -1028,7 +1028,7 @@ null == undefined // true # `7. Черновик: Практики эффективности` -## Наши соавторы работают над этим разделом. [Хотите присоединиться?](Https://github.com/i0natan/nodebestpractices/issues/256) +## Наши соавторы работают над этим разделом. [Хотите присоединиться?](Https://github.com/goldbergyoni/nodebestpractices/issues/256)

@@ -1057,7 +1057,7 @@ null == undefined // true # Вехи -Чтобы поддерживать это руководство и обновлять его, мы постоянно обновляем и совершенствуем рекомендации и лучшие практики с помощью сообщества. Вы можете следить за нашими [вехами](https://github.com/i0natan/nodebestpractices/milestones) и присоединиться к рабочим группам, если хотите внести свой вклад в этот проект. +Чтобы поддерживать это руководство и обновлять его, мы постоянно обновляем и совершенствуем рекомендации и лучшие практики с помощью сообщества. Вы можете следить за нашими [вехами](https://github.com/goldbergyoni/nodebestpractices/milestones) и присоединиться к рабочим группам, если хотите внести свой вклад в этот проект.
@@ -1073,21 +1073,21 @@ null == undefined // true ### Переводы в процессе -- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/129)) -- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/i0natan/nodebestpractices/issues/156)) -- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Любезно предоставлено [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/94)) -- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/i0natan/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/i0natan/nodebestpractices/issues/95)) -- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/i0natan/nodebestpractices/issues/139)) +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Любезно предоставлено [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139))

## Руководящий комитет -Познакомьтесь с членами руководящего комитета -- людьми, которые работают вместе, чтобы обеспечить управление и дальнейшее руководство проектом. Кроме того, каждый член комитета руководит проектом, отслеживаемым в рамках наших [проектов Github](https://github.com/i0natan/nodebestpractices/projects). +Познакомьтесь с членами руководящего комитета -- людьми, которые работают вместе, чтобы обеспечить управление и дальнейшее руководство проектом. Кроме того, каждый член комитета руководит проектом, отслеживаемым в рамках наших [проектов Github](https://github.com/goldbergyoni/nodebestpractices/projects). -[Yoni Goldberg](https://github.com/i0natan) +[Yoni Goldberg](https://github.com/goldbergyoni) diff --git a/sections/errorhandling/apmproducts.brazilian-portuguese.md b/sections/errorhandling/apmproducts.brazilian-portuguese.md index 886424ded..1e7b824cb 100644 --- a/sections/errorhandling/apmproducts.brazilian-portuguese.md +++ b/sections/errorhandling/apmproducts.brazilian-portuguese.md @@ -22,7 +22,7 @@ Os produtos APM constituem 3 segmentos principais: ### Exemplo: UpTimeRobot.Com - Painel de monitoramento de site -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Painel de monitoramento de sites") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Painel de monitoramento de sites") ### Example: AppDynamics.Com – monitoramento de ponta a ponta combinado com instrumentação de código -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "monitoramento de ponta a ponta combinado com instrumentação de código") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "monitoramento de ponta a ponta combinado com instrumentação de código") diff --git a/sections/errorhandling/apmproducts.chinese.md b/sections/errorhandling/apmproducts.chinese.md index eed5aecc3..8803f2791 100644 --- a/sections/errorhandling/apmproducts.chinese.md +++ b/sections/errorhandling/apmproducts.chinese.md @@ -21,7 +21,7 @@ APM 产品由3个主要部分构成: ### 示例: UpTimeRobot.Com – 网站监控仪表板 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") ### 示例: AppDynamics.Com – 与代码检测结合的端到端监视 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/apmproducts.korean.md b/sections/errorhandling/apmproducts.korean.md index 2861667e8..ad5081797 100644 --- a/sections/errorhandling/apmproducts.korean.md +++ b/sections/errorhandling/apmproducts.korean.md @@ -22,7 +22,7 @@ APM products constitute 3 major segments: ### Example: UpTimeRobot.Com – Website monitoring dashboard -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") ### Example: AppDynamics.Com – end to end monitoring combined with code instrumentation -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/apmproducts.md b/sections/errorhandling/apmproducts.md index c0ad2150e..5f302168d 100644 --- a/sections/errorhandling/apmproducts.md +++ b/sections/errorhandling/apmproducts.md @@ -22,7 +22,7 @@ APM products constitute 3 major segments: ### Example: UpTimeRobot.Com – Website monitoring dashboard -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") ### Example: AppDynamics.Com – end to end monitoring combined with code instrumentation -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/apmproducts.polish.md b/sections/errorhandling/apmproducts.polish.md index 13474a2c8..a53a4b176 100644 --- a/sections/errorhandling/apmproducts.polish.md +++ b/sections/errorhandling/apmproducts.polish.md @@ -22,7 +22,7 @@ Produkty APM stanowią 3 główne segmenty: ### Przykład: UpTimeRobot.Com – Website monitoring dashboard -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") ### Przykład: AppDynamics.Com – end to end monitoring combined with code instrumentation -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/apmproducts.russian.md b/sections/errorhandling/apmproducts.russian.md index 137aec1ab..242b6786c 100644 --- a/sections/errorhandling/apmproducts.russian.md +++ b/sections/errorhandling/apmproducts.russian.md @@ -22,7 +22,7 @@ ### Пример: UpTimeRobot.Com - панель мониторинга сайта -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Панель мониторинга сайта") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Панель мониторинга сайта") ### Пример: AppDynamics.Com – сквозной мониторинг в сочетании с инструментарием кода -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "Сквозной мониторинг в сочетании с инструментарием кода") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "Сквозной мониторинг в сочетании с инструментарием кода") diff --git a/sections/errorhandling/documentingusingswagger.brazilian-portuguese.md b/sections/errorhandling/documentingusingswagger.brazilian-portuguese.md index fe208d712..4af6a9890 100644 --- a/sections/errorhandling/documentingusingswagger.brazilian-portuguese.md +++ b/sections/errorhandling/documentingusingswagger.brazilian-portuguese.md @@ -49,4 +49,4 @@ Do blog Joyent, classificado como 1 para as palavras-chave “Node.js logging” ### Ferramenta Útil: Swagger Criação de Documentação Online -![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "lidando com erros API") +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "lidando com erros API") diff --git a/sections/errorhandling/documentingusingswagger.chinese.md b/sections/errorhandling/documentingusingswagger.chinese.md index e7f5addfa..5d3998ac1 100644 --- a/sections/errorhandling/documentingusingswagger.chinese.md +++ b/sections/errorhandling/documentingusingswagger.chinese.md @@ -13,4 +13,4 @@ REST API使用HTTP代码返回结果, API用户不仅绝对需要了解API schem ### 有用的工具: Swagger 在线文档创建工具 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") \ No newline at end of file +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") \ No newline at end of file diff --git a/sections/errorhandling/documentingusingswagger.korean.md b/sections/errorhandling/documentingusingswagger.korean.md index b71cfead5..3341b0c13 100644 --- a/sections/errorhandling/documentingusingswagger.korean.md +++ b/sections/errorhandling/documentingusingswagger.korean.md @@ -49,4 +49,4 @@ From the blog Joyent, ranked 1 for the keywords “Node.js logging” ### Useful Tool: Swagger Online Documentation Creator -![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/errorhandling/documentingusingswagger.md b/sections/errorhandling/documentingusingswagger.md index b71cfead5..3341b0c13 100644 --- a/sections/errorhandling/documentingusingswagger.md +++ b/sections/errorhandling/documentingusingswagger.md @@ -49,4 +49,4 @@ From the blog Joyent, ranked 1 for the keywords “Node.js logging” ### Useful Tool: Swagger Online Documentation Creator -![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/errorhandling/documentingusingswagger.polish.md b/sections/errorhandling/documentingusingswagger.polish.md index 7720ca2a4..e274fea45 100644 --- a/sections/errorhandling/documentingusingswagger.polish.md +++ b/sections/errorhandling/documentingusingswagger.polish.md @@ -49,4 +49,4 @@ Z bloga Joyent, w rankingu 1 dla słów kluczowych “Node.js logging” ### Przydatne narzędzie: Swagger Online Documentation Creator -![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/errorhandling/documentingusingswagger.russian.md b/sections/errorhandling/documentingusingswagger.russian.md index 525d14897..2086e312f 100644 --- a/sections/errorhandling/documentingusingswagger.russian.md +++ b/sections/errorhandling/documentingusingswagger.russian.md @@ -49,4 +49,4 @@ API-интерфейсы REST возвращают результаты с ис ### Полезный инструмент: Swagger Online Documentation Creator -![Swagger API Scheme](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/projectstructre/breakintcomponents.brazilian-portuguese.md b/sections/projectstructre/breakintcomponents.brazilian-portuguese.md index df18e206b..660b0be9d 100644 --- a/sections/projectstructre/breakintcomponents.brazilian-portuguese.md +++ b/sections/projectstructre/breakintcomponents.brazilian-portuguese.md @@ -27,10 +27,10 @@ Então, o que a arquitetura da sua aplicação grita? Quando você olha para a e ### Bom: estruture sua solução por componentes independentes -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Solução de estruturação por componentes") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Solução de estruturação por componentes")

### Ruim: Agrupe seus arquivos por papel técnico -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Solução de estruturação por funções técnicas") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Solução de estruturação por funções técnicas") diff --git a/sections/projectstructre/breakintcomponents.chinese.md b/sections/projectstructre/breakintcomponents.chinese.md index 1de6e5561..d4ae21dff 100644 --- a/sections/projectstructre/breakintcomponents.chinese.md +++ b/sections/projectstructre/breakintcomponents.chinese.md @@ -27,10 +27,10 @@

### 推荐: 通过独立组件构建解决方案 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### 避免: 按技术角色对文件进行分组 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/breakintcomponents.japanese.md b/sections/projectstructre/breakintcomponents.japanese.md index f6afe33c4..2190132c1 100644 --- a/sections/projectstructre/breakintcomponents.japanese.md +++ b/sections/projectstructre/breakintcomponents.japanese.md @@ -71,7 +71,7 @@ So what does the architecture of your application scream? When you look at the t ### GOOD:自己完結型のコンポーネントでソリューションを構築する -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

@@ -81,4 +81,4 @@ So what does the architecture of your application scream? When you look at the t ### BAD:技術的な役割別でファイルをグループ化してしまう -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/breakintcomponents.korean.md b/sections/projectstructre/breakintcomponents.korean.md index 7912668ef..a35f23509 100644 --- a/sections/projectstructre/breakintcomponents.korean.md +++ b/sections/projectstructre/breakintcomponents.korean.md @@ -28,10 +28,10 @@ MartinFowler.com 블로그로부터 ### 좋은예: 자족적인 컴포넌트 기반으로 설계하라 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### 나쁜예: 파일을 기술적인 역할별로 모아라 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/breakintcomponents.md b/sections/projectstructre/breakintcomponents.md index 4b1c0b36d..69cf5761c 100644 --- a/sections/projectstructre/breakintcomponents.md +++ b/sections/projectstructre/breakintcomponents.md @@ -28,10 +28,10 @@ So what does the architecture of your application scream? When you look at the t ### Good: Structure your solution by self-contained components -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### Bad: Group your files by technical role -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/breakintcomponents.polish.md b/sections/projectstructre/breakintcomponents.polish.md index c9650bc21..62098e97b 100644 --- a/sections/projectstructre/breakintcomponents.polish.md +++ b/sections/projectstructre/breakintcomponents.polish.md @@ -27,10 +27,10 @@ Więc co krzyczy architektura twojej aplikacji? Gdy spojrzysz na strukturę kata ### Dobre: Skonstruuj swoje rozwiązanie według samodzielnych komponentów -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### Złe: Pogrupuj pliki według roli technicznej -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/breakintcomponents.russian.md b/sections/projectstructre/breakintcomponents.russian.md index b6935d20d..7e5c36706 100644 --- a/sections/projectstructre/breakintcomponents.russian.md +++ b/sections/projectstructre/breakintcomponents.russian.md @@ -28,10 +28,10 @@ ### Хорошо: структурируйте свое решение по отдельным компонентам -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### Плохо: сгруппируйте файлы по техническим ролям -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/createlayers.brazilian-portuguese.md b/sections/projectstructre/createlayers.brazilian-portuguese.md index 78e181a67..8990640f5 100644 --- a/sections/projectstructre/createlayers.brazilian-portuguese.md +++ b/sections/projectstructre/createlayers.brazilian-portuguese.md @@ -4,10 +4,10 @@ ### Separe o código do componente em camadas: web, serviços e DAL -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separe o código do componente em camadas") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separe o código do componente em camadas")

### Explicação em 1 minuto: A desvantagem de misturar camadas -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "A desvantagem de misturar camadas") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "A desvantagem de misturar camadas") diff --git a/sections/projectstructre/createlayers.chinese.md b/sections/projectstructre/createlayers.chinese.md index 6a0866330..0ab1cc1e9 100644 --- a/sections/projectstructre/createlayers.chinese.md +++ b/sections/projectstructre/createlayers.chinese.md @@ -3,9 +3,9 @@

### 将组件代码分成web, services, DAL层 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers")

### 1分钟说明:混合层的缺点 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/createlayers.korean.md b/sections/projectstructre/createlayers.korean.md index c6731556b..c1d89a628 100644 --- a/sections/projectstructre/createlayers.korean.md +++ b/sections/projectstructre/createlayers.korean.md @@ -4,10 +4,10 @@ ### 컴포넌트 코드를 웹, 서비스, 데이터 접근 언어(DAL) 계층으로 나누어라 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers")

### 1분 설명: 계층을 섞으면 불리한 점 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/createlayers.md b/sections/projectstructre/createlayers.md index 029924a57..f6f860c12 100644 --- a/sections/projectstructre/createlayers.md +++ b/sections/projectstructre/createlayers.md @@ -4,10 +4,10 @@ ### Separate component code into layers: web, services, and DAL -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers")

### 1 min explainer: The downside of mixing layers -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/createlayers.polish.md b/sections/projectstructre/createlayers.polish.md index e0fd1fe67..6c068cd8e 100644 --- a/sections/projectstructre/createlayers.polish.md +++ b/sections/projectstructre/createlayers.polish.md @@ -4,10 +4,10 @@ ### Rozdziel kod komponentu na warstwy: sieć, usługi i DAL -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers")

### 1 minuta wyjaśniania: Minusem mieszanie warstw -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/createlayers.russian.md b/sections/projectstructre/createlayers.russian.md index 8b0383087..7579e0465 100644 --- a/sections/projectstructre/createlayers.russian.md +++ b/sections/projectstructre/createlayers.russian.md @@ -4,10 +4,10 @@ ### Разделить код компонента на слои: веб, сервисы и DAL -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers")

### 1 минутное объяснение: обратная сторона смешения слоев -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/thincomponents.chinese.md b/sections/projectstructre/thincomponents.chinese.md index d55704b02..6fc193ed3 100644 --- a/sections/projectstructre/thincomponents.chinese.md +++ b/sections/projectstructre/thincomponents.chinese.md @@ -18,9 +18,9 @@

### 推荐: 通过自包含的组件来构造解决方案 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### 避免: 通过技术角色对文件进行分组 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/thincomponents.md b/sections/projectstructre/thincomponents.md index b248637b4..7d5535b6e 100644 --- a/sections/projectstructre/thincomponents.md +++ b/sections/projectstructre/thincomponents.md @@ -18,10 +18,10 @@ For medium sized apps and above, monoliths are really bad - one big software wit ### Good: Structure your solution by self-contained components -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### Bad: Group your files by technical role -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/thincomponents.russian.md b/sections/projectstructre/thincomponents.russian.md index 4aa6baecb..394f3dbe6 100644 --- a/sections/projectstructre/thincomponents.russian.md +++ b/sections/projectstructre/thincomponents.russian.md @@ -18,10 +18,10 @@ ### Хорошо: структурируйте свое решение по отдельным компонентам -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components")

### Плохо: сгруппируйте файлы по техническим ролям -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/wraputilities.brazilian-portuguese.md b/sections/projectstructre/wraputilities.brazilian-portuguese.md index 95b643bc1..c054b4d04 100644 --- a/sections/projectstructre/wraputilities.brazilian-portuguese.md +++ b/sections/projectstructre/wraputilities.brazilian-portuguese.md @@ -10,4 +10,4 @@ Quando você começa a crescer e tem componentes diferentes em servidores difere ### Compartilhando seus próprios utilitários comuns em ambientes e componentes -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Solução de estruturação por componentes") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Solução de estruturação por componentes") diff --git a/sections/projectstructre/wraputilities.chinese.md b/sections/projectstructre/wraputilities.chinese.md index 1d3f14457..15511bf97 100644 --- a/sections/projectstructre/wraputilities.chinese.md +++ b/sections/projectstructre/wraputilities.chinese.md @@ -11,4 +11,4 @@ ### 在环境和组件中共享你自己的公用实用工具 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "构建解决方案的组件") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "构建解决方案的组件") diff --git a/sections/projectstructre/wraputilities.korean.md b/sections/projectstructre/wraputilities.korean.md index f06ec4a1f..9265ea317 100644 --- a/sections/projectstructre/wraputilities.korean.md +++ b/sections/projectstructre/wraputilities.korean.md @@ -10,4 +10,4 @@ ### 당신만의 공유 유틸리티들을 환경과 컴포넌츠에 공유하기 -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/projectstructre/wraputilities.md b/sections/projectstructre/wraputilities.md index 4b52ff6db..ed3639007 100644 --- a/sections/projectstructre/wraputilities.md +++ b/sections/projectstructre/wraputilities.md @@ -10,4 +10,4 @@ Once you start growing and have different components on different servers which ### Sharing your own common utilities across environments and components -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/projectstructre/wraputilities.polish.md b/sections/projectstructre/wraputilities.polish.md index 42470bd36..e823509de 100644 --- a/sections/projectstructre/wraputilities.polish.md +++ b/sections/projectstructre/wraputilities.polish.md @@ -10,4 +10,4 @@ Kiedy zaczniesz się rozwijać i będziesz mieć różne komponenty na różnych ### Udostępnianie własnych wspólnych narzędzi w różnych środowiskach i komponentach -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/projectstructre/wraputilities.russian.md b/sections/projectstructre/wraputilities.russian.md index 25d79804c..02a1fac70 100644 --- a/sections/projectstructre/wraputilities.russian.md +++ b/sections/projectstructre/wraputilities.russian.md @@ -10,4 +10,4 @@ ### Совместное использование собственных общих утилит в средах и компонентах -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/template.md b/sections/template.md index cebd6affc..d4c154f29 100644 --- a/sections/template.md +++ b/sections/template.md @@ -34,15 +34,15 @@ code here ### Example: Complex methods analysis with CodeClimate (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") ### Example: Code analysis trends and history with CodeClimate (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") ### Example: Code analysis summary and trends with SonarQube (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history")

diff --git a/sections/testingandquality/3-parts-in-name.brazilian-portuguese.md b/sections/testingandquality/3-parts-in-name.brazilian-portuguese.md index 2aae9e1c6..97ddc0479 100644 --- a/sections/testingandquality/3-parts-in-name.brazilian-portuguese.md +++ b/sections/testingandquality/3-parts-in-name.brazilian-portuguese.md @@ -49,6 +49,6 @@ describe('Serviço de Produtos', function() { [Do blog "30 Node.js testing best practices" por Yoni Goldberg](https://medium.com/@me_37286/yoni-goldberg-javascript-nodejs-testing-best-practices-2b98924c9347) - ![Um exemplo de relatório de teste](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "Um exemplo de relatório de teste") + ![Um exemplo de relatório de teste](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "Um exemplo de relatório de teste")

\ No newline at end of file diff --git a/sections/testingandquality/3-parts-in-name.md b/sections/testingandquality/3-parts-in-name.md index 5f36d01d2..570e179e7 100644 --- a/sections/testingandquality/3-parts-in-name.md +++ b/sections/testingandquality/3-parts-in-name.md @@ -49,6 +49,6 @@ describe('Products Service', () => { [From the blog "30 Node.js testing best practices" by Yoni Goldberg](https://medium.com/@me_37286/yoni-goldberg-javascript-nodejs-testing-best-practices-2b98924c9347) - ![A test report example](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example") + ![A test report example](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example")

\ No newline at end of file diff --git a/sections/testingandquality/3-parts-in-name.polish.md b/sections/testingandquality/3-parts-in-name.polish.md index d5b0a3572..ca6beff51 100644 --- a/sections/testingandquality/3-parts-in-name.polish.md +++ b/sections/testingandquality/3-parts-in-name.polish.md @@ -49,6 +49,6 @@ describe('Products Service', () => { [Z bloga "30 Node.js testing best practices" od Yoni Goldberg](https://medium.com/@me_37286/yoni-goldberg-javascript-nodejs-testing-best-practices-2b98924c9347) - ![Przykład raportu testu](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example") + ![Przykład raportu testu](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example")

diff --git a/sections/testingandquality/3-parts-in-name.russian.md b/sections/testingandquality/3-parts-in-name.russian.md index 5337a84ff..17c4131c0 100644 --- a/sections/testingandquality/3-parts-in-name.russian.md +++ b/sections/testingandquality/3-parts-in-name.russian.md @@ -49,6 +49,6 @@ describe('Products Service', () => { [From the blog "30 Node.js testing best practices" by Yoni Goldberg](https://medium.com/@me_37286/yoni-goldberg-javascript-nodejs-testing-best-practices-2b98924c9347) - ![A test report example](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example") + ![A test report example](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/test-report-like-requirements.jpeg "A test report example")

\ No newline at end of file diff --git a/sections/testingandquality/bumpversion.md b/sections/testingandquality/bumpversion.md index ef848aae7..56e66ff1d 100644 --- a/sections/testingandquality/bumpversion.md +++ b/sections/testingandquality/bumpversion.md @@ -24,4 +24,4 @@ code here ### Image title -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/testingandquality/citools.brazilian-portuguese.md b/sections/testingandquality/citools.brazilian-portuguese.md index 92a155737..741a5cb80 100644 --- a/sections/testingandquality/citools.brazilian-portuguese.md +++ b/sections/testingandquality/citools.brazilian-portuguese.md @@ -42,10 +42,10 @@ jobs: ### Circle CI - CI com quase zero configuração em nuvem -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "manipulador de erros do API") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "manipulador de erros do API") ### Jenkins - CI sofisticado e robusto -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "manipulador de erros do API") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "manipulador de erros do API")

diff --git a/sections/testingandquality/citools.chinese.md b/sections/testingandquality/citools.chinese.md index dc2e35671..d58ef8403 100644 --- a/sections/testingandquality/citools.chinese.md +++ b/sections/testingandquality/citools.chinese.md @@ -44,10 +44,10 @@ jobs: ### Circle CI - 几乎零设置的云CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") ### Jenkins - 完善和强大的CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")

diff --git a/sections/testingandquality/citools.korean.md b/sections/testingandquality/citools.korean.md index ae381495c..96eb69110 100644 --- a/sections/testingandquality/citools.korean.md +++ b/sections/testingandquality/citools.korean.md @@ -42,10 +42,10 @@ jobs: ### Circle CI - almost zero setup cloud CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") ### Jenkins - sophisticated and robust CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")

diff --git a/sections/testingandquality/citools.md b/sections/testingandquality/citools.md index 26edad066..16c6c16ae 100644 --- a/sections/testingandquality/citools.md +++ b/sections/testingandquality/citools.md @@ -42,10 +42,10 @@ jobs: ### Circle CI - almost zero setup cloud CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") ### Jenkins - sophisticated and robust CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")

diff --git a/sections/testingandquality/citools.polish.md b/sections/testingandquality/citools.polish.md index cefaa3dd3..ae98f0a69 100644 --- a/sections/testingandquality/citools.polish.md +++ b/sections/testingandquality/citools.polish.md @@ -42,10 +42,10 @@ jobs: ### Circle CI - prawie zerowa konfiguracja CI w chmurze -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") ### Jenkins - wyrafinowany i solidny CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")

diff --git a/sections/testingandquality/citools.russian.md b/sections/testingandquality/citools.russian.md index 3ccf1f24f..f46905269 100644 --- a/sections/testingandquality/citools.russian.md +++ b/sections/testingandquality/citools.russian.md @@ -42,10 +42,10 @@ jobs: ### Circle CI - почти нулевая настройка облака CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/circleci.png "API error handling") ### Дженкинс - сложный и надежный CI -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/jenkins_dashboard.png "API error handling")

diff --git a/sections/testingandquality/refactoring.brazilian-portuguese.md b/sections/testingandquality/refactoring.brazilian-portuguese.md index 7b543222e..490266eda 100644 --- a/sections/testingandquality/refactoring.brazilian-portuguese.md +++ b/sections/testingandquality/refactoring.brazilian-portuguese.md @@ -27,15 +27,15 @@ A refatoração é um processo importante no fluxo de desenvolvimento iterativo. ### Exemplo: Análise de métodos complexos com CodeClimate (comercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Análise de métodos complexos") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Análise de métodos complexos") ### Exemplo: tendências de análise de código e histórico com CodeClimate (comercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Histórico de análise de código") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Histórico de análise de código") ### Exemplo: Resumo de análise de código e tendências com o SonarQube (comercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Histórico de análise de código") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Histórico de análise de código")

diff --git a/sections/testingandquality/refactoring.md b/sections/testingandquality/refactoring.md index 50c1c5693..99a506edc 100644 --- a/sections/testingandquality/refactoring.md +++ b/sections/testingandquality/refactoring.md @@ -29,15 +29,15 @@ will always be an issue if the underlying quality of your JavaScript is poor. ### Example: Complex methods analysis with CodeClimate (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") ### Example: Code analysis trends and history with CodeClimate (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") ### Example: Code analysis summary and trends with SonarQube (commercial) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history")

diff --git a/sections/testingandquality/refactoring.polish.md b/sections/testingandquality/refactoring.polish.md index 1bba53299..6e2fce6f6 100644 --- a/sections/testingandquality/refactoring.polish.md +++ b/sections/testingandquality/refactoring.polish.md @@ -29,15 +29,15 @@ will always be an issue if the underlying quality of your JavaScript is poor. ### Przykład: Analiza złożonych metod za pomocą CodeClimate (komercyjna) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") ### Przykład: trendy i historia analizy kodu za pomocą CodeClimate (komercyjna) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") ### Przykład: Podsumowanie analizy kodu i trendy w SonarQube (komercyjna) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history")

diff --git a/sections/testingandquality/refactoring.russian.md b/sections/testingandquality/refactoring.russian.md index 74e0c9013..5693544c9 100644 --- a/sections/testingandquality/refactoring.russian.md +++ b/sections/testingandquality/refactoring.russian.md @@ -29,15 +29,15 @@ ### Пример: Анализ сложных методов с помощью CodeClimate (коммерческий) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-complex-methods.PNG "Complex methods analysis") ### Пример: Тенденции и история анализа кода с CodeClimate (коммерческий) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-climate-history.PNG "Code analysis history") ### Пример: Сводка анализа кода и тенденции с SonarQube (коммерческий) -![alt text](https://github.com/i0natan/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history") +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/codeanalysis-sonarqube-dashboard.PNG "Code analysis history")

From f37d4a5cfacecf082fe1d598d2cbe8bfe4e6ad71 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 03:32:03 +0000 Subject: [PATCH 0302/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c3810d8fc..bb4207fb8 100644 --- a/README.md +++ b/README.md @@ -1303,6 +1303,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
keyrwinz

🖋
Dmitry Nikitenko

🖋
bushuai

👀 🖋 +
Benjamin Gruenbaum

🖋 From d4ccc68355e0e69c26cb037f93d35da8d96659f9 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 6 Jun 2020 03:32:04 +0000 Subject: [PATCH 0303/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 124b7f67b..6e3f96d7e 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -924,6 +924,15 @@ "review", "content" ] + }, + { + "login": "benjamingr", + "name": "Benjamin Gruenbaum", + "avatar_url": "https://avatars2.githubusercontent.com/u/1315533?v=4", + "profile": "https://stackoverflow.com/users/1348195/benjamin-gruenbaum", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 13653c71bdf1a2e516692d9d77b7917d32f6dabd Mon Sep 17 00:00:00 2001 From: Kyle Martin Date: Sun, 7 Jun 2020 17:26:51 +1200 Subject: [PATCH 0304/1795] scaffold list --- README.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/README.md b/README.md index 896343e0c..2d87bcd65 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines 5. [Going To Production Practices (19) ](#5-going-to-production-practices) 6. [Security Practices (25)](#6-security-best-practices) 7. [Performance Practices (2) (Work In Progress️ ✍️)](#7-draft-performance-best-practices) +8. [Docker Practices (Work In Progress️ ✍️)](#7-draft-docker-best-practices)

@@ -1072,6 +1073,176 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


+

⬆ Return to top

+ +# `7. Draft: Docker Best Practices` + +## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/682) + +

+ +## ![✔] 8.1. Clean npm cache + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Clean npm cache**](/sections/docker/file.md) + +


+ +## ![✔] 8.2. Bootstrap the code using 'node' command, avoid 'npm run' scripts + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Clean npm cache**](/sections/docker/file.md) + +


+ +## ![✔] 8.3. Install packages for production + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Bootstrap the code using 'node' command, avoid 'npm run' scripts**](/sections/docker/file.md) + +


+ +## ![✔] 8.4. Lint your Dockerfile + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Lint your Dockerfile**](/sections/docker/file.md) + +


+ +## ![✔] 8.5. Utilize caching for better build time + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Utilize caching for better build time**](/sections/docker/file.md) + +


+ +## ![✔] 8.6. Set Docker memory limits which are in-par with v8 memory limit + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Set Docker memory limits which are in-par with v8 memory limit**](/sections/docker/file.md) + +


+ +## ![✔] 8.7. Scan your image for vulnerabilities + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/file.md) + +


+ +## ![✔] 8.8. Use multistage builds + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Use multistage builds**](/sections/docker/file.md) + +


+ +## ![✔] 8.9. Don't use "latest" tags, use a digest + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Don't use "latest", use a digest**](/sections/docker/file.md) + +


+ +## ![✔] 8.10. Prefer smaller images + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Prefer smaller images**](/sections/docker/file.md) + +


+ +## ![✔] 8.11. Graceful shutdown + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Graceful shutdown**](/sections/docker/file.md) + +


+ +## ![✔] 8.12. Avoid sending secrets as build time arguments + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Avoid sending secrets as build time arguments**](/sections/docker/file.md) + +


+ +## ![✔] 8.13. On the importance of docker ignore + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: On the importance of docker ignore**](/sections/docker/file.md) + +


+ +## ![✔] 8.14. Avoid inconsistent images + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Avoid inconsistent images**](/sections/docker/file.md) + +


+ +## ![✔] 8.15. Avoid process managers + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Avoid process managers**](/sections/docker/file.md) + +


+ +## ![✔] 8.16. Generic Docker practices + +**TL;DR:** + +**Otherwise:** + +🔗 [**Read More: Generic Docker practices**](/sections/docker/file.md) + +


+ +

⬆ Return to top

+ # Milestones To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/goldbergyoni/nodebestpractices/milestones) and join the working groups if you want to contribute to this project From 275c75a3f2c54cdb594c30a921b46ab380d6a108 Mon Sep 17 00:00:00 2001 From: blackmatch Date: Sun, 7 Jun 2020 22:56:46 +0800 Subject: [PATCH 0305/1795] style: add spaces between English and Chinese --- sections/security/saferedirects.chinese.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sections/security/saferedirects.chinese.md b/sections/security/saferedirects.chinese.md index 4a756894f..5dd687669 100644 --- a/sections/security/saferedirects.chinese.md +++ b/sections/security/saferedirects.chinese.md @@ -2,9 +2,10 @@ ### 一段解释 -当我们在Node.js或者Express中实现重定向时,在服务器端进行输入校验非常重要。当攻击者发现你没有校验用户提供的外部输入时,他们会在论坛、社交媒体以和其他公共场合发布他们精心制作的链接来诱使用户点击,以此达到漏洞利用的目的。 +当我们在 Node.js 或者 Express 中实现重定向时,在服务器端进行输入校验非常重要。当攻击者发现你没有校验用户提供的外部输入时,他们会在论坛、社交媒体以和其他公共场合发布他们精心制作的链接来诱使用户点击,以此达到漏洞利用的目的。 + +案例: express 使用用户输入的不安全的重定向 -案例:express使用用户输入的不安全的重定向 ```javascript const express = require('express'); const app = express(); @@ -21,6 +22,7 @@ app.get('/login', (req, res, next) => { 建议的避免不安全重定向的方案是,避免依赖用户输入的内容来进行重定向。如果一定要使用用户输入的内容,可以通过使用白名单重定向的方式来避免暴露漏洞。 案例:使用白名单实现安全的重定向 + ```javascript const whitelist = { 'https://google.com': 1 @@ -49,7 +51,9 @@ app.get('/login', (req, res, next) => { ### 其他博主的看法 来自博客[NodeSwat](https://blog.nodeswat.com/unvalidated-redirects-b0a2885720db): + > 幸运的是,缓解此漏洞的方法非常简单-不要使用未经验证的用户输入作为重定向的基础。 来自博客[Hailstone](https://blog.hailstone.io/how-to-prevent-unsafe-redirects-in-node-js/): + > 但是,如果服务器端的重定向逻辑没有对url参数的数据进行校验的话,则你的用户可能最终访问的地址跟你的地址看起来几乎完全一致(examp1e.com),但这最终满足了犯罪黑客们的需求。 From 2de9c084cf067492f315f2de8d51c96d8750f786 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 7 Jun 2020 21:44:34 +0300 Subject: [PATCH 0306/1795] First draft of avoiding secrets --- README.md | 15 ++-- sections/docker/avoid-build-time-secrets.md | 83 +++++++++++++++++++++ 2 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 sections/docker/avoid-build-time-secrets.md diff --git a/README.md b/README.md index 2d87bcd65..6300589a6 100644 --- a/README.md +++ b/README.md @@ -1191,13 +1191,14 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.12. Avoid sending secrets as build time arguments +## ![✔] 8.12. Clean-out build-time secrets, avoid secrets in args -**TL;DR:** +**TL;DR:** Avoid secrets leaking from the docker build environment. A docker image is typically shared in multiple environment, like CI and a registry, that are not as sanitized as production. A typical example is a npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like .npmrc and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces -**Otherwise:** -🔗 [**Read More: Avoid sending secrets as build time arguments**](/sections/docker/file.md) +**Otherwise:** Everyone with access to the CI and docker registry will also get as a bonus access to some precious organization secrets + +🔗 [**Read More: Clean-out build-time secrets**](/sections/docker/avoid-build-time-secrets.md)


@@ -1320,9 +1321,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md new file mode 100644 index 000000000..b2362de3c --- /dev/null +++ b/sections/docker/avoid-build-time-secrets.md @@ -0,0 +1,83 @@ +# Clean build-time secrets, avoid secrets as args + +

+ +### One Paragraph Explainer + +A docker image is not just a bunch of files rather constitutes multiple layers that reveal also what happened during the build time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however in fact this token can now be fetched from the developers machine Docker history, from the Docker registry and the CI. An attacker who get access to that token, is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not prevent the secrets file from appearing in the local docker history but considered as as secured enough in some organizations. + +

+ +### Code Example – Using Docker mounted secrets (experimental but stable) + +
+Dockerfile +``` +# syntax = docker/dockerfile:1.0-experimental + +FROM node:12-slim +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN --mount=type=secret,id=npm,target=/root/.npmrc npm ci + +# The rest comes here + +``` +
+ +

+ +### Code Example – Building securely using multi-stage build +
+Dockerfile + +``` + +FROM node:12-slim AS build +ARG NPM_TOKEN +WORKDIR /usr/src/app +COPY . /dist +RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ + npm ci --production && \ + rm -f .npmrc + +FROM build as prod +COPY --from=build /dist /dist +CMD ["node","index.js"] + +# The ARG and .npmrc won't appear in the final image, but can be found in the Docker daemon un-tagged images list - make sure to delete those + +``` +
+ +

+ +### Code Example Anti Pattern – Using build time args + +
+Dockerfile + +``` + +FROM node:12-slim +ARG NPM_TOKEN +WORKDIR /usr/src/app +COPY . /dist +RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ + npm ci --production && \ + rm -f .npmrc + +# Deleting the .npmrc within the same copy command will not save it inside the layer, however it can be found in image history + +CMD ["node","index.js"] + +``` +
+

+ +### Blog Quote: "These secrets aren’t saved in the final Docker" + +From the blog, [Alexandra Ulsh](https://www.alexandraulsh.com/2019/02/24/docker-build-secrets-and-npmrc/?fbclid=IwAR0EAr1nr4_QiGzlNQcQKkd9rem19an9atJRO_8-n7oOZXwprToFQ53Y0KQ) + +> In November 2018 Docker 18.09 introduced a new --secret flag for docker build. This allows us to pass secrets from a file to our Docker builds. These secrets aren’t saved in the final Docker image, any intermediate images, or the image commit history. With build secrets, you can now securely build Docker images with private npm packages without build arguments and multi-stage builds. +``` From 95218010cc699e9bf69926ace063c41326f0ad31 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 7 Jun 2020 21:48:45 +0300 Subject: [PATCH 0307/1795] First draft of avoiding secrets --- sections/docker/avoid-build-time-secrets.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md index b2362de3c..940915e79 100644 --- a/sections/docker/avoid-build-time-secrets.md +++ b/sections/docker/avoid-build-time-secrets.md @@ -12,6 +12,7 @@ A docker image is not just a bunch of files rather constitutes multiple layers t
Dockerfile + ``` # syntax = docker/dockerfile:1.0-experimental @@ -23,14 +24,17 @@ RUN --mount=type=secret,id=npm,target=/root/.npmrc npm ci # The rest comes here ``` +


### Code Example – Building securely using multi-stage build
+ Dockerfile + ``` FROM node:12-slim AS build @@ -48,6 +52,7 @@ CMD ["node","index.js"] # The ARG and .npmrc won't appear in the final image, but can be found in the Docker daemon un-tagged images list - make sure to delete those ``` +


@@ -55,6 +60,7 @@ CMD ["node","index.js"] ### Code Example Anti Pattern – Using build time args
+ Dockerfile ``` @@ -72,7 +78,9 @@ RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ CMD ["node","index.js"] ``` +
+

### Blog Quote: "These secrets aren’t saved in the final Docker" From 40a50c4560776db7d1827e79ba9f9cd86ee3c07c Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 7 Jun 2020 21:50:01 +0300 Subject: [PATCH 0308/1795] First draft of avoiding secrets2 --- sections/docker/avoid-build-time-secrets.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md index 940915e79..6f0a5619b 100644 --- a/sections/docker/avoid-build-time-secrets.md +++ b/sections/docker/avoid-build-time-secrets.md @@ -30,11 +30,11 @@ RUN --mount=type=secret,id=npm,target=/root/.npmrc npm ci

### Code Example – Building securely using multi-stage build +
Dockerfile - ``` FROM node:12-slim AS build @@ -88,4 +88,7 @@ CMD ["node","index.js"] From the blog, [Alexandra Ulsh](https://www.alexandraulsh.com/2019/02/24/docker-build-secrets-and-npmrc/?fbclid=IwAR0EAr1nr4_QiGzlNQcQKkd9rem19an9atJRO_8-n7oOZXwprToFQ53Y0KQ) > In November 2018 Docker 18.09 introduced a new --secret flag for docker build. This allows us to pass secrets from a file to our Docker builds. These secrets aren’t saved in the final Docker image, any intermediate images, or the image commit history. With build secrets, you can now securely build Docker images with private npm packages without build arguments and multi-stage builds. + +``` + ``` From c3053294e330464d4f7be7a1ca3a4135882ca191 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 7 Jun 2020 21:51:10 +0300 Subject: [PATCH 0309/1795] First draft of avoiding secrets2 --- sections/docker/avoid-build-time-secrets.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md index 6f0a5619b..e9a34bc81 100644 --- a/sections/docker/avoid-build-time-secrets.md +++ b/sections/docker/avoid-build-time-secrets.md @@ -11,6 +11,7 @@ A docker image is not just a bunch of files rather constitutes multiple layers t ### Code Example – Using Docker mounted secrets (experimental but stable)
+ Dockerfile ``` @@ -22,7 +23,6 @@ COPY package.json package-lock.json ./ RUN --mount=type=secret,id=npm,target=/root/.npmrc npm ci # The rest comes here - ```
@@ -50,7 +50,6 @@ COPY --from=build /dist /dist CMD ["node","index.js"] # The ARG and .npmrc won't appear in the final image, but can be found in the Docker daemon un-tagged images list - make sure to delete those - ```
@@ -76,7 +75,6 @@ RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ # Deleting the .npmrc within the same copy command will not save it inside the layer, however it can be found in image history CMD ["node","index.js"] - ``` From 3af0b7295ea7a75c9a6fb09379d20bfbd91c9f00 Mon Sep 17 00:00:00 2001 From: Yoni Date: Mon, 8 Jun 2020 14:57:31 +0300 Subject: [PATCH 0310/1795] 1st draft --- README.md | 12 ++++- .../docker/restart-and-replicate-processes.md | 44 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 sections/docker/restart-and-replicate-processes.md diff --git a/README.md b/README.md index 2d87bcd65..949bf764f 100644 --- a/README.md +++ b/README.md @@ -1239,7 +1239,17 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E 🔗 [**Read More: Generic Docker practices**](/sections/docker/file.md) -


+


+ +## ![✔] 8.17. Let the Docker orchestrator restart and replicate processes + +**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediating process managers or custom code that replicate the process (e.g., Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes + +**Otherwise:** Container keeps crashing due to lack of resources will get restarted indifiently by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance + +🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](/sections/docker/restart-and-replicate-processes.md) + +


⬆ Return to top

diff --git a/sections/docker/restart-and-replicate-processes.md b/sections/docker/restart-and-replicate-processes.md new file mode 100644 index 000000000..d9ed157d1 --- /dev/null +++ b/sections/docker/restart-and-replicate-processes.md @@ -0,0 +1,44 @@ +# Let the Docker orchestrator restart and replicate processes + +

+ +### One Paragraph Explainer + +Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and can conclude many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that, some may tempt to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side, When using local tools for restarting the process, the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone. + +

+ +### Code Example – Invoking Node.js directly without intermediating tools + +
+ +Dockerfile + +``` + +FROM node:12-slim + +# The build logic comes here + +CMD ["node", "index.js"] +``` + +
+ +

+ +### Code Example Anti Pattern – Using a process manager + +
+ +Dockerfile + +``` +FROM node:12-slim + +# The build logic comes here + +CMD ["pm2-runtime", "indes.js"] +``` + +
From a687a0a418d088ca8334970a2e9c13eb954882db Mon Sep 17 00:00:00 2001 From: Yoni Date: Wed, 10 Jun 2020 17:09:30 +0300 Subject: [PATCH 0311/1795] First draft --- README.md | 12 +- ...Kubernetes-graceful-shutdown-flowchart.png | Bin 0 -> 22357 bytes sections/docker/graceful-shutdown.md | 115 ++++++++++++++++++ 3 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 assets/images/Kubernetes-graceful-shutdown-flowchart.png create mode 100644 sections/docker/graceful-shutdown.md diff --git a/README.md b/README.md index 2d87bcd65..c38924d69 100644 --- a/README.md +++ b/README.md @@ -1183,11 +1183,11 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.11. Graceful shutdown -**TL;DR:** +**TL;DR:** Handle the process SIGTERM event and clean-up all existing conenction and resources. This should be done while responding to ongoing reqeusts. In Dockerized runtimes, shuting down containers is not a rare event rather a frequent occurence that happen as part of routine work. Acheiving this demand some thoughful code to orchestrate few moving parts: The load balancer, keep-alive connections, the HTTP server and other resources -**Otherwise:** +**Otherwise:** Dying immediately means not responding to thousands of disappointed users -🔗 [**Read More: Graceful shutdown**](/sections/docker/file.md) +🔗 [**Read More: Graceful shutdown**](/sections/docker/graceful-shutdown.md)


@@ -1320,9 +1320,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/assets/images/Kubernetes-graceful-shutdown-flowchart.png b/assets/images/Kubernetes-graceful-shutdown-flowchart.png new file mode 100644 index 0000000000000000000000000000000000000000..5772b387f919c9340ab09939cd61a5d47de4280d GIT binary patch literal 22357 zcmbrlbySq^*FHLkf`W91Gzik&Ae{p;qaZB}LrHfif^>K1z(`4ViFCuzsr1k>ARzD? z{e0s+@A>_6)_IuqJa=4s?`zl1tXVT*uQZf!uqd$r0054PvOEX?cmx9g(C!|iqgL{A zoeKeg2iLFEUn`)de=aJrf1X?S!+?hm{u%%0e*d2l0Pvsje+K_!^M4Qj!Titr|3>^X zx8wuhK1L1yA4p67e^!o`TmCEc@5O(%Q22jd|8olXi$UH0tIeAGUmE_s_#cJ;(eS?p z{{a4I_^WOEe&o;C>JOzqmzBVubxVPN0RKz&U&udze{ld<@?B~x{1HKE_-FAyyO#W> zw%UIb5@TsHhk^Yb^Y6`_#B!^5aN-lwJgL(kIEa_K$T5cMjs3j_ief`SGI2Zy}9 ziSco7Q&YDrEnNaF?}0#L9Vscvvp~xtGc)?7n{ zR0lOVI$}S0yn5LyBFD$c^P+LMfQgBzx3~BELIseE>pVW*PDaMl)3d|bIXF0Y!r#As zDL>QDu(PwXus1%tGb-`zTSR1}wU?oyqT=jP$4hmwUx|qkVDNfq=t~xs;6(Sg78<7F z;%IE-^?L9vtb|vQlRT4k<~y$4a-g#W@wR>vefSxbxv)Qm z@h`O}c`HK}9sV+!oguy+^3O!;iujD%7SYBeEIZfj*$l793} z0R+PJWhTXB3)7?N2`z%Ts)!7IT8@r7R~_NO5QxHWP2#)X9WQP8kv-q`=f*nwPdA4< zIyz1}xU-KB008iN6?vK0o(ubMdNcstQ@~e!I?zyvpFfZ2@nf3VLB5DxGcovOZRF0^B6{1!V;5a2+(NTA8Rd?9RNz)l9J-kV zshb6pWaATTj=sUxZlPzOFVoG8CHpD`gkTubd57@oT6okzzorX$$*nE4pue(4FNt{b z06+W7I}nl9o&0;FYX%^z_!s})Y^!;(4gkwsnxzN0>zO-CTnWQ?gl4xIK%r_B(3=TSniF(%TTJqvAN zan35G&5E{{WXo^O)_ZBqI7h#%$zl;~CRpw*g`BjC-6k=)5oQhD3TvxJU*qu7oO84+ zat5y4<*eEr;x6^C7g3YBy4A<&qU@+q*+~jUOJ4n~`{1Bd7N>?7Co0(A<4#*X{Gh@( zEf;qiOcp{%TL7I;?T3+wl2 zrp%^dlnFg3bJLcwC@X`DH>w~Goh&@WF*NO5w4Uo#Ha;Q~;9?6JqgUc6vd@|p=GGEO7 zDDkY8ZR}292i}W6zI&+c!y>R^{H5J~Na)VE{GOd!9>m^v4TzwGIyc1smUe6Xr~;Jt zBCUJZW~@>cP^C7Sfj^_E8DE&KBxSlzJHPz&h*EoO^lRV6$82(D36sZD99XJL#9=1bPt57NG`dE62Uq8F(sJtJ>%c9=*~Ts)&57YBGfIAFf5okO%QRIJtjreq!|sUsf6IP1Ads@I+Oh53q

e14*z((W%%;T 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 literal 0 HcmV?d00001 diff --git a/sections/docker/graceful-shutdown.md b/sections/docker/graceful-shutdown.md new file mode 100644 index 000000000..bf73a8d68 --- /dev/null +++ b/sections/docker/graceful-shutdown.md @@ -0,0 +1,115 @@ +# Shutdown gracefully + +

+ +### One Paragraph Explainer + +In a Dockerized runtime like Kubernetes, containers are born and die frequently. This happens not only when errors are thrown but also for good reasons like relocating containers, replacing them with a newer version and more. It's achieved by sending a notice (SIGTERM signal) to the process with a 30 second grace period. This puts a challenge on the developer to ensure the app is handling the ongoing requests and clean-up resources in a timely fashion. Otherwise, thousands of sad users will not get a response. Implementation-wise, the shutdown code should wait until all ongoing requests are flushed out and then clean-up resources. Easier said than done, practically it demands orchestrating few parts: Tell the LoadBalancer that the app is not ready to server more requests (via health-check), wait that existing requests are done, avoid handling new requests, clean-up resources and finally log some useful information before dying. If Keep-Alive connections are being used, the clients also must be notified that a new connection should be established - A library like [Stoppable](https://github.com/hunterloftis/stoppable) can greatly help achieving this. + +

+ +### Code Example – A graceful shutdown when using Express and Kubernetes + +

+ +JavaScript + +``` +TBD +``` + +
+ +

+ +### Code Example – Harsh shutdown + +
+ +JavaScript + +``` +TBD +``` + +
+ +

+ +### Code Example – Making Node.js the root process allowing passing signals to the code + +
+ +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +CMD ["node", "index.js"] +#This line above will make Node.js the root process (PID1) + +``` + +
+ +

+ +### Code Example – Using Tiny process manager to forward signals to Node + +
+ +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini +RUN chmod +x /tini +ENTRYPOINT ["/tini", "--"] + +CMD ["node", "index.js"] +#Now Node will run a sub-process of TINI which acts as PID1 + +``` + +
+ +

+ +### Code Example Anti Pattern – Using npm scripts to initialize the process + +
+ +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +CMD ["npm", "start"] +#Now Node will run a sub-process of npm and won't receive signals + +``` + +
+ +

+ +### Example - The shutdown phases + +From the blog, [Rising Stack](https://blog.risingstack.com/graceful-shutdown-node-js-kubernetes/) + +![alt text](/assets/images/Kubernetes-graceful-shutdown-flowchart.png "The shutdown phases") + +``` + +``` From 687d834ee6480baffb52186ae70d0f4c4234db10 Mon Sep 17 00:00:00 2001 From: Yoni Date: Thu, 11 Jun 2020 11:58:03 +0300 Subject: [PATCH 0312/1795] First draft --- README.md | 14 +++--- sections/docker/install-for-production.md | 54 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 sections/docker/install-for-production.md diff --git a/README.md b/README.md index 2d87bcd65..89e069185 100644 --- a/README.md +++ b/README.md @@ -1101,13 +1101,13 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.3. Install packages for production +## ![✔] 8.3. Remove development dependencies -**TL;DR:** +**TL;DR:** Althoug DevDepencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development depdencies. Doing so gurantess that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' -**Otherwise:** +**Otherwise:** Many of the infamous npm security breaches were found within development packages -🔗 [**Read More: Bootstrap the code using 'node' command, avoid 'npm run' scripts**](/sections/docker/file.md) +🔗 [**Read More: Remove development dependencies**](/sections/docker/install-for-production.md)


@@ -1320,9 +1320,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md new file mode 100644 index 000000000..6e33a4d4b --- /dev/null +++ b/sections/docker/install-for-production.md @@ -0,0 +1,54 @@ +# Remove development dependencies + +

+ +### One Paragraph Explainer + +Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependecies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons, the image that is finally shipped to production should be safe and minimal. Running npm install with a --production is a great start however it get even safer to run 'npm ci' that ensure a fresh install and the existence of lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, multi stage builds can help in having different sets of dependencies and finally only those for production (See dedicated bullet on multi stage builds) + +

+ +### Code Example – Installing for production + +
+ +Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# The rest comes here +``` + +
+ +

+ +### Code Example Anti-Pattern – Installing all dependencies + +
+ +Dockerfile + +``` + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm install + +# The rest comes here +``` + +
+ +

+ +### Blog Quote: "npm ci is also more strict than a regular install" + +From [npm documentation](https://docs.npmjs.com/cli/ci.html) + +> This command is similar to npm-install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment – or any situation where you want to make sure you’re doing a clean install of your dependencies. It can be significantly faster than a regular npm install by skipping certain user-oriented features. It is also more strict than a regular install, which can help catch errors or inconsistencies caused by the incrementally-installed local environments of most npm users. From 9dd5faf1b42e6cd239c50a67af06a62d5621f30c Mon Sep 17 00:00:00 2001 From: Yoni Date: Fri, 12 Jun 2020 14:10:30 +0300 Subject: [PATCH 0313/1795] Genric message --- assets/images/anchore-report.png | Bin 0 -> 125328 bytes sections/docker/scan-images.md | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 assets/images/anchore-report.png create mode 100644 sections/docker/scan-images.md diff --git a/assets/images/anchore-report.png b/assets/images/anchore-report.png new file mode 100644 index 0000000000000000000000000000000000000000..d733c438ef0ff914e1355d0bc7b8cd8dc8149b23 GIT binary patch literal 125328 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
kMu#ZmIA~u-M&KS@jOVcrN#@zE$gjBpIAeR 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 XJZK_U;j`h+2k_;v!O5=B#yk3V75)V} literal 0 HcmV?d00001 diff --git a/sections/docker/scan-images.md b/sections/docker/scan-images.md new file mode 100644 index 000000000..d8add4af6 --- /dev/null +++ b/sections/docker/scan-images.md @@ -0,0 +1,30 @@ +# Scan the entire image before production + +

+ +### One Paragraph Explainer + +Scanning the code vunlerabilities is obviously a valuable act but it doesn't cover all potential threats. Why? Because CVEs exist also on the OS level and your code might execute a vunlerable version of Shell, Tarball or OpenSSL. In addition, vunbelerable dependencies might be injected after the code scan (i.e. supply chain attacks) hence a last comprehensive scan of the final deliberable image is in order. This resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the overall. There are more than a handful of tools that scans the entire Docker image, Scanners comes in 3 main flavours: Local/CI binaries with a cached vunlerabilities DB, scanners as a service in the cloud and a niche of tools who can scan during the docker build itself. The first group is the most popular and usually the faster, tools like Trivvy, Anchore and Snyk are worth exploring. Most CI vendors provide a local plugin that facilitate the interaction with these scanners. It should be noted that these type of scanners cover a lot of ground and therefore will find CVEs in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed + +

+ +### Code Example – Scanning with Trivvy + +
+ +Bash + +``` +sudo apt-get install rpm +$ wget https://github.com/aquasecurity/trivy/releases/download/{TRIVY_VERSION}/trivy_{TRIVY_VERSION}_Linux-64bit.deb +$ sudo dpkg -i trivy_{TRIVY_VERSION}_Linux-64bit.deb +trivy image [YOUR_IMAGE_NAME] +``` + +
+ +

+ +### Report Example – Docker scan results (By Anchore) + +![Report examples](/assets/images/anchore-report.png "Docker scan report") From def07fa01c70a5845051b77d8b385393d417df85 Mon Sep 17 00:00:00 2001 From: Yoni Date: Fri, 12 Jun 2020 15:14:02 +0300 Subject: [PATCH 0314/1795] Scan images - First draft --- README.md | 6 +++--- sections/docker/scan-images.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2d87bcd65..4659b2b7e 100644 --- a/README.md +++ b/README.md @@ -1143,11 +1143,11 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.7. Scan your image for vulnerabilities -**TL;DR:** +**TL;DR:** Besides checking code dependencies vulnerabilities, also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected a vulnerability during the build. Consequently, it recommended running this as the last step before deployment. There a handful of free and commercial scanners that also provide CI/CD plugins -**Otherwise:** +**Otherwise:** Your code might be entirely free from vulnerabilities. However, it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that commonly being used by applications -🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/file.md) +🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/scan-images.md)


diff --git a/sections/docker/scan-images.md b/sections/docker/scan-images.md index d8add4af6..8f3411d16 100644 --- a/sections/docker/scan-images.md +++ b/sections/docker/scan-images.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Scanning the code vunlerabilities is obviously a valuable act but it doesn't cover all potential threats. Why? Because CVEs exist also on the OS level and your code might execute a vunlerable version of Shell, Tarball or OpenSSL. In addition, vunbelerable dependencies might be injected after the code scan (i.e. supply chain attacks) hence a last comprehensive scan of the final deliberable image is in order. This resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the overall. There are more than a handful of tools that scans the entire Docker image, Scanners comes in 3 main flavours: Local/CI binaries with a cached vunlerabilities DB, scanners as a service in the cloud and a niche of tools who can scan during the docker build itself. The first group is the most popular and usually the faster, tools like Trivvy, Anchore and Snyk are worth exploring. Most CI vendors provide a local plugin that facilitate the interaction with these scanners. It should be noted that these type of scanners cover a lot of ground and therefore will find CVEs in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed +Scanning the code for vulnerabilities is a valuable act, but it doesn't cover all the potential threats. Why? Because vulnerabilities also exist on the OS level and the app might execute those binaries like Shell, Tarball, OpenSSL. Also, vulnerable dependencies might be injected after the code scan (i.e. supply chain attacks) - hence scanning the final image just before production is in order. This idea resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the assmebled deliverable. There are 3 main scanner families: Local/CI binaries with a cached vulnerabilities DB, scanners as a service in the cloud and a niche of tools which scan during the docker build itself. The first group is the most popular and usually the fastest - Tools like [Trivvy](https://github.com/aquasecurity/trivy), [Anchore](https://github.com/anchore/anchore) and [Snyk](https://support.snyk.io/hc/en-us/articles/360003946897-Container-security-overview) are worth exploring. Most CI vendors provide a local plugin that facilitates the interaction with these scanners. It should be noted that these scanners cover a lot of ground and therefore will show findings in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed

From 8a0a66af67dac659946a809939c5830f883eda09 Mon Sep 17 00:00:00 2001 From: Yoni Date: Fri, 12 Jun 2020 15:16:57 +0300 Subject: [PATCH 0315/1795] Scan images - First draft --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4659b2b7e..b60b6f202 100644 --- a/README.md +++ b/README.md @@ -1143,9 +1143,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.7. Scan your image for vulnerabilities -**TL;DR:** Besides checking code dependencies vulnerabilities, also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected a vulnerability during the build. Consequently, it recommended running this as the last step before deployment. There a handful of free and commercial scanners that also provide CI/CD plugins +**TL;DR:** Besides checking code dependencies vulnerabilities, also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins -**Otherwise:** Your code might be entirely free from vulnerabilities. However, it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that commonly being used by applications +**Otherwise:** Your code might be entirely free from vulnerabilities. However, it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications 🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/scan-images.md) From e7f6f596ff593852305eec5d2b1bf9d34a10e523 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 13:04:42 +0300 Subject: [PATCH 0316/1795] Memory limit - 1st draft --- README.md | 6 +-- sections/docker/memory-limit.md | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 sections/docker/memory-limit.md diff --git a/README.md b/README.md index 2d87bcd65..431435c94 100644 --- a/README.md +++ b/README.md @@ -1131,11 +1131,11 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.6. Set Docker memory limits which are in-par with v8 memory limit +## ![✔] 8.6. Set memory limits using Docker only -**TL;DR:** +**TL;DR:** Configure a memory limit using Docker, not using V8. Use Docker command flag 'run --memory' or set the right values within the platform that runs Docker. By doing this, the runtime will be capable of making better decisions on when to scale, prevent one citizen from starving others, drive thoughtful crash decisions (e.g., Docker can allow slight burst deviations) and in-overall it's always better to move HW decisions to the OPS court -**Otherwise:** +**Otherwise:** When setting limits using V8 --max-old-space-size the Docker runtime won't be aware of its capacity limits and will have to blindly place it in an instance that might not have the right size 🔗 [**Read More: Set Docker memory limits which are in-par with v8 memory limit**](/sections/docker/file.md) diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md new file mode 100644 index 000000000..583891519 --- /dev/null +++ b/sections/docker/memory-limit.md @@ -0,0 +1,76 @@ +# Set memory limits using Docker only + +

+ +### One Paragraph Explainer + +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime. There is confusion around which of those to set, some configure both. The better option would be to set the limit on the Docker command or the Docker runtime (e.g. Kubernetes) as is has a much wider perspective for making the right decisions. First, given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Docker also measures the actually used memory and not the allocated part so it can get Killed only when really needed. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. On the contrary, when setting the limit on V8 - each of the described scenarios will not be supported. + +

+ +### Code Example – Memory limit with Docker + +
+Bash + +``` +docker run --memory 512m my-node-app +``` + +
+ +

+ +### Code Example – Memory limit with Kubernetes + +
+Kubernetes deployment yaml + +``` +apiVersion: v1 +kind: Pod +metadata: + name: my-node-app +spec: + containers: + - name: my-node-app + image: my-node-app + resources: + requests: + memory: "400Mi" + limits: + memory: "500Mi" + command: ["node index.js"] +``` + +
+ +

+ +### Code Example Anti-Pattern – Setting memory limit using V8 flags + +
+ +Kubernetes deployment yaml + +``` +apiVersion: v1 +kind: Pod +metadata: + name: my-node-app +spec: + containers: + - name: my-node-app + image: my-node-app + command: ["node index.js --max-old-space-size=400"] +``` + +
+ +

+ +### Kubernetes documentation: "If you do not specify a memory limit" + +From [K8S documentation](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/?fbclid=IwAR2gfRL-3UB2WIbxJNSl3jNdlEJF3rAU5WG049yNorLOVSVngmRY1xhrbqg#if-you-do-not-specify-a-memory-limit) + +> The Container has no upper bound on the amount of memory it uses. The Container could use all of the memory available on the Node where it is running which in turn could invoke the OOM Killer. Further, in case of an OOM Kill, a container with no resource limits will have a greater chance of being killed. From e7c8e06a8f6d351bd6ca8cf4b97cbd1eaa41043d Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 13:06:27 +0300 Subject: [PATCH 0317/1795] Memory limit - 1st draft --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 431435c94..e65e66213 100644 --- a/README.md +++ b/README.md @@ -1137,7 +1137,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **Otherwise:** When setting limits using V8 --max-old-space-size the Docker runtime won't be aware of its capacity limits and will have to blindly place it in an instance that might not have the right size -🔗 [**Read More: Set Docker memory limits which are in-par with v8 memory limit**](/sections/docker/file.md) +🔗 [**Read More: Set Docker memory limits which are in-par with v8 memory limit**](/sections/docker/memory-limit.md)


@@ -1320,9 +1320,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -|
| | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators From 0e1e15c7945251642c8105800bb746ec31635bc4 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 13:06:58 +0300 Subject: [PATCH 0318/1795] Memory limit - 1st draft --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e65e66213..1dfd8ea5e 100644 --- a/README.md +++ b/README.md @@ -1137,7 +1137,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **Otherwise:** When setting limits using V8 --max-old-space-size the Docker runtime won't be aware of its capacity limits and will have to blindly place it in an instance that might not have the right size -🔗 [**Read More: Set Docker memory limits which are in-par with v8 memory limit**](/sections/docker/memory-limit.md) +🔗 [**Read More: Set memory limits using Docker only**](/sections/docker/memory-limit.md)


From 8dfd73fc9be3656ce91178aa0a30622c1b51efd1 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 17:10:37 +0300 Subject: [PATCH 0319/1795] Remove cache - first draft --- README.md | 15 ++++++++------- sections/docker/clean-cache.md | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 sections/docker/clean-cache.md diff --git a/README.md b/README.md index 2d87bcd65..23f01a5c2 100644 --- a/README.md +++ b/README.md @@ -1081,13 +1081,14 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E

-## ![✔] 8.1. Clean npm cache +## ![✔] 8.1. Clean NODE_MODULE cache -**TL;DR:** +**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies packages to speed future installs since there won't be any further installs - A docker image is created only once. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved -**Otherwise:** -🔗 [**Read More: Clean npm cache**](/sections/docker/file.md) +**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used + +🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md)


@@ -1320,9 +1321,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/sections/docker/clean-cache.md b/sections/docker/clean-cache.md new file mode 100644 index 000000000..625ca40a2 --- /dev/null +++ b/sections/docker/clean-cache.md @@ -0,0 +1,25 @@ +# Clean NODE_MODULE cache + +

+ +### One Paragraph Explainer + +Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container, this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag + +

+ +### Code Example – Clean cache + +
+Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# The rest comes here +``` + +
\ No newline at end of file From e6608e6142ad236824f2791c264245182444caff Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 20:49:05 +0300 Subject: [PATCH 0320/1795] Docker ignore file - 1st draft --- README.md | 14 +++++----- sections/docker/docker-ignore.md | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) create mode 100644 sections/docker/docker-ignore.md diff --git a/README.md b/README.md index 2d87bcd65..5f025307a 100644 --- a/README.md +++ b/README.md @@ -1201,13 +1201,13 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.13. On the importance of docker ignore +## ![✔] Use .dockerignore to prevent leaking secrets -**TL;DR:** +**TL;DR:** Include a .dockerignore file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus, the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker -**Otherwise:** +**Otherwise:** Common personal secret files like .env, .aws and .npmrc will be shared with anybody with access to the image (e.g. Docker repository) -🔗 [**Read More: On the importance of docker ignore**](/sections/docker/file.md) +🔗 [**Read More: On the importance of docker ignore**](/sections/docker/docker-ignore.md)


@@ -1320,9 +1320,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md new file mode 100644 index 000000000..0289babbc --- /dev/null +++ b/sections/docker/docker-ignore.md @@ -0,0 +1,48 @@ +# Use .dockerignore to prevent leaking secrets + +

+ +### One Paragraph Explainer + +The Docker build command copies the local files into the build context environment over a virtual network. Cautious, development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world, the Dockerfile should be explicit about what is being copied. On top of this, include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the build can better utilize the cache and achieve better performance + +

+ +### Code Example – A good default .dockerignore for Node.js apps + +
+.dockerignore + +``` +**/node_modules/ +**/.git +**/README.md +**/LICENSE +**/.vscode +**/npm-debug.log +**/coverage +**/.env +**/.editorconfig +**/.aws +``` + +
+ +

+ +### Code Example Anti-Pattern – Recursive copy of all files + +
+Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +# The next line copies everything +COPY . . + +# The rest comes here + +``` + +
From 44814868a62c990a258c7d5b369fe06bd14db9f8 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 14 Jun 2020 21:36:25 +0300 Subject: [PATCH 0321/1795] Generic advice - 1st draft --- README.md | 11 +++++------ sections/docker/generic-tips.md | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 sections/docker/generic-tips.md diff --git a/README.md b/README.md index 2d87bcd65..c97bc8ee3 100644 --- a/README.md +++ b/README.md @@ -1233,11 +1233,10 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.16. Generic Docker practices -**TL;DR:** +**TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. -**Otherwise:** -🔗 [**Read More: Generic Docker practices**](/sections/docker/file.md) +🔗 [**Read More: Generic Docker practices**](/sections/docker/generic-tips.md)


@@ -1320,9 +1319,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | ### Past collaborators diff --git a/sections/docker/generic-tips.md b/sections/docker/generic-tips.md new file mode 100644 index 000000000..de8836ba4 --- /dev/null +++ b/sections/docker/generic-tips.md @@ -0,0 +1,29 @@ +[✔]: ../../assets/images/checkbox-small-blue.png + +# Common Node.js Docker best practices + +This common Docker guidelines section contains best practices that are standardized among all programming languages and have no special Node.js interpretation + +## ![✔] Prefer COPY over ADD command + +**TL;DR:** COPY is safer as it copies local files only while ADD supports fancier fetches like downloading binaries from remote sites + +## ![✔] Avoid updating the base OS + +**TL;DR:** Updating the local binaries during build (e.g. apt-get update) creates inconsistent images every time it runs and also demands high privilige. Instead use base images that are relatively updated + +## ![✔] Tag images using labels + +**TL;DR:** Providing meta-data for each image might help Ops professional treat it adequately. For example, include the maintainer name, build date and other information that might prove useful when someone need to reason about an image + +## ![✔] Use unprivileged containers + +**TL;DR:** Privileged container have the same permissions and capabilities as the root user over the host machine. This is rarely needed and as a rule of thumb should be avoided + +## ![✔] Inspect and verify the final result + +**TL;DR:** Sometimes it's easy to overlook side effects in the build process like leaked secrets or unnecessary files. Inspecting the produced image using tools like [Dive](https://github.com/wagoodman/dive) can easily help to identify such issues + +## ![✔] Perform integrity check + +**TL;DR:** While pulling base or final images, the network might be mislead and redirected to download malicious images. Nothing in the standard Docker protocol prevents this unless signing and verifying the content. [Docker Notary](https://docs.docker.com/notary/getting_started/) is one of the tools to achieve this From 3699c39ec9fd20f60541c3713b7a928961868745 Mon Sep 17 00:00:00 2001 From: Kyle Martin Date: Sat, 27 Jun 2020 19:50:36 +1200 Subject: [PATCH 0322/1795] WIP --- README.md | 14 ++++++++------ sections/docker/image-tags.md | 18 ++++++++++++++++++ sections/docker/lint-dockerfile.md | 23 +++++++++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 sections/docker/image-tags.md create mode 100644 sections/docker/lint-dockerfile.md diff --git a/README.md b/README.md index 2d87bcd65..a93ee18bf 100644 --- a/README.md +++ b/README.md @@ -1113,9 +1113,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.4. Lint your Dockerfile -**TL;DR:** +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified. -**Otherwise:** +**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user. 🔗 [**Read More: Lint your Dockerfile**](/sections/docker/file.md) @@ -1161,13 +1161,15 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.9. Don't use "latest" tags, use a digest +## ![✔] 8.9. Understand image tags, and use the "latest" tag with caution -**TL;DR:** +**TL;DR:** The latest tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. -**Otherwise:** +In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a determinstic install is expected, a SHA256 digest can be used to reference an exact image. + +**Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. -🔗 [**Read More: Don't use "latest", use a digest**](/sections/docker/file.md) +🔗 [**Read More: Understand image tags, and use the "latest" tag with caution**](/sections/docker/file.md)


diff --git a/sections/docker/image-tags.md b/sections/docker/image-tags.md new file mode 100644 index 000000000..2cf221462 --- /dev/null +++ b/sections/docker/image-tags.md @@ -0,0 +1,18 @@ +# Understand image tags, and use the "latest" tag with caution + +### One Paragraph Explainer + + +### Code example: + +``` + +``` + + + +### What Other Bloggers Say +From the blog [name](https://example.com): +> A quote + +
diff --git a/sections/docker/lint-dockerfile.md b/sections/docker/lint-dockerfile.md new file mode 100644 index 000000000..b4ce9342e --- /dev/null +++ b/sections/docker/lint-dockerfile.md @@ -0,0 +1,23 @@ +# Lint your Dockerfile + +### One Paragraph Explainer + +As our core application code is linted to conform to best practices and eliminate issues and bugs before it could become a problem, so too should our Dockerfiles. Linting the Dockerfile means you can ensure that there aren’t any structural problems with the logic and instructions specified in your Dockerfiles. + +
+ +### Code example: +The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialised Dockerfile linter that aims to embrace the [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) + +```bash +hadolint production.Dockerfile +hadolint --ignore DL3003 --ignore DL3006 # exclude specific rules +hadolint --trusted-registry my-company.com:500 # Warn when using untrusted FROM images +``` + +### What Other Bloggers Say + +From the blog [Josh Reichardt](https://thepracticalsysadmin.com/lint-your-dockerfiles-with-hadolint/): +> If you haven’t already gotten in to the habit of linting your Dockerfiles you should. Code linting is a common practice in software development which helps find, identify and eliminate issues and bugs before they are ever able to become a problem. One of the main benefits of linting your code is that it helps identify and eliminate nasty little bugs before they ever have a chance to become a problem. + +
From 4a0f7dfb7535a98234103c596bd7db6b293459ac Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:56:45 +0000 Subject: [PATCH 0323/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 896343e0c..df16e9bb9 100644 --- a/README.md +++ b/README.md @@ -1304,6 +1304,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
Dmitry Nikitenko

🖋
bushuai

👀 🖋
Benjamin Gruenbaum

🖋 +
Ezequiel

🌍 From 60669a17fe23eebf04521c076077d0875e4fd84b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:56:46 +0000 Subject: [PATCH 0324/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 6e3f96d7e..eec1cead6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -933,6 +933,15 @@ "contributions": [ "content" ] + }, + { + "login": "byeze", + "name": "Ezequiel", + "avatar_url": "https://avatars1.githubusercontent.com/u/7424138?v=4", + "profile": "https://github.com/byeze", + "contributions": [ + "translation" + ] } ], "projectName": "nodebestpractices", From 3d2baeb82836ccf4f3351f5b198962873243ec90 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:58:46 +0000 Subject: [PATCH 0325/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index df16e9bb9..ea9319293 100644 --- a/README.md +++ b/README.md @@ -1305,6 +1305,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
bushuai

👀 🖋
Benjamin Gruenbaum

🖋
Ezequiel

🌍 +
Juan José Rodríguez

🌍 From b2ab3d96f9165f03ba5be66dec9d87164d5c8f4e Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 29 Jun 2020 14:58:47 +0000 Subject: [PATCH 0326/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index eec1cead6..db3e96feb 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -942,6 +942,15 @@ "contributions": [ "translation" ] + }, + { + "login": "juaoose", + "name": "Juan José Rodríguez", + "avatar_url": "https://avatars3.githubusercontent.com/u/994594?v=4", + "profile": "https://github.com/juaoose", + "contributions": [ + "translation" + ] } ], "projectName": "nodebestpractices", From 720cb5a6b14905841793d8a1fb65790b3430afa0 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 5 Jul 2020 11:26:21 +0300 Subject: [PATCH 0327/1795] Fixed TYPO --- sections/docker/docker-ignore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md index 0289babbc..6f5bf9e56 100644 --- a/sections/docker/docker-ignore.md +++ b/sections/docker/docker-ignore.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -The Docker build command copies the local files into the build context environment over a virtual network. Cautious, development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world, the Dockerfile should be explicit about what is being copied. On top of this, include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the build can better utilize the cache and achieve better performance +The Docker build command copies the local files into the build context environment over a virtual network. Be careful - development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world, the Dockerfile should be explicit about what is being copied. On top of this, include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the build can better utilize the cache and achieve better performance

From 55265f891ec60decb568efdbebe2dbaaaac140bc Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 5 Jul 2020 11:29:21 +0300 Subject: [PATCH 0328/1795] Fixed TYPO --- sections/docker/docker-ignore.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md index 6f5bf9e56..21a97a146 100644 --- a/sections/docker/docker-ignore.md +++ b/sections/docker/docker-ignore.md @@ -8,7 +8,7 @@ The Docker build command copies the local files into the build context environme

-### Code Example – A good default .dockerignore for Node.js apps +### Code Example – A good default .dockerignore for Node.js
.dockerignore From 82c298d2559d91acb167be152d868bd3fe88d9f1 Mon Sep 17 00:00:00 2001 From: Or Bin Date: Sun, 5 Jul 2020 22:54:40 +0300 Subject: [PATCH 0329/1795] Typo fix: who's -> whose --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea9319293..5da2f4016 100644 --- a/README.md +++ b/README.md @@ -994,7 +994,7 @@ All statements above will return false if used with `===` **TL;DR:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. -**Otherwise:** [Have you heard about the eslint developer who's password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) +**Otherwise:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8)

From 06f8d26f3c6eb6890fa3e4b518bf3bd915ca43d7 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 06:59:21 +0000 Subject: [PATCH 0330/1795] docs: update README.md [skip ci] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ea9319293..06f2da608 100644 --- a/README.md +++ b/README.md @@ -1307,6 +1307,9 @@ Thanks goes to these wonderful people who have contributed to this repository!
Ezequiel

🌍
Juan José Rodríguez

🌍 + +
Or Bin

🖋 + From 38848d148a30bb48eb0375f6650ad551add4bd16 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2020 06:59:22 +0000 Subject: [PATCH 0331/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index db3e96feb..4e26b3f55 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -951,6 +951,15 @@ "contributions": [ "translation" ] + }, + { + "login": "OrBin", + "name": "Or Bin", + "avatar_url": "https://avatars1.githubusercontent.com/u/6897234?v=4", + "profile": "https://github.com/OrBin", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From dddfe6d9224b4e0ce835625e49b7cfd14d46683b Mon Sep 17 00:00:00 2001 From: Kyle Martin Date: Tue, 7 Jul 2020 19:48:33 +1200 Subject: [PATCH 0332/1795] WIP --- README.md | 10 +++++----- sections/docker/image-tags.md | 25 ++++++++++++++++++------- sections/docker/lint-dockerfile.md | 6 ++++-- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index a93ee18bf..737aff046 100644 --- a/README.md +++ b/README.md @@ -1113,11 +1113,11 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.4. Lint your Dockerfile -**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified. +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. **Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user. -🔗 [**Read More: Lint your Dockerfile**](/sections/docker/file.md) +🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md)


@@ -1161,15 +1161,15 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.9. Understand image tags, and use the "latest" tag with caution +## ![✔] 8.9. Understand image tags vs digests, and use the "latest" tag with caution -**TL;DR:** The latest tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. +**TL;DR:** The latest tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a determinstic install is expected, a SHA256 digest can be used to reference an exact image. **Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. -🔗 [**Read More: Understand image tags, and use the "latest" tag with caution**](/sections/docker/file.md) +🔗 [**Read More: Understand image tags, and use the "latest" tag with caution**](/sections/docker/image-tags.md)


diff --git a/sections/docker/image-tags.md b/sections/docker/image-tags.md index 2cf221462..4584b12ce 100644 --- a/sections/docker/image-tags.md +++ b/sections/docker/image-tags.md @@ -1,18 +1,29 @@ -# Understand image tags, and use the "latest" tag with caution +# Understand image tags vs digests, and use the "latest" tag with caution ### One Paragraph Explainer +If this is a production situation, and security and stability are important, then just "convenience" is likely not the best deciding factor. -### Code example: +In addition, the 'latest' tag is Docker's default tag. This means that a developer who forgets to add an explicit tag, will accidentally push a new version of an image as 'latest', which might end in very unintended results if the latest tag is being relied upon as the latest production image. -``` +### Code example: +```bash +$ docker build -t company/image_name:0.1 . +# :latest image is not updated +$ docker build -t company/image_name +# :latest image is updated +$ docker build -t company/image_name:0.2 . +# :latest image is not updated +$ docker build -t company/image_name:latest . +# :latest image is updated ``` - - ### What Other Bloggers Say -From the blog [name](https://example.com): -> A quote +From the blog by [Vladislav Supalov](https://vsupalov.com/docker-latest-tag/): +> Some people expect that :latest always points to the most-recently-pushed version of an image. That’s not true. + +From the [Docker success center](https://success.docker.com/article/images-tagging-vs-digests) +>
diff --git a/sections/docker/lint-dockerfile.md b/sections/docker/lint-dockerfile.md index b4ce9342e..8f70cb981 100644 --- a/sections/docker/lint-dockerfile.md +++ b/sections/docker/lint-dockerfile.md @@ -7,7 +7,7 @@ As our core application code is linted to conform to best practices and eliminat
### Code example: -The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialised Dockerfile linter that aims to embrace the [Docker best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialised Dockerfile linter that aims to embrace the [Docker best practices.](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) ```bash hadolint production.Dockerfile @@ -17,7 +17,9 @@ hadolint --trusted-registry my-company.com:500 # Warn when using un ### What Other Bloggers Say -From the blog [Josh Reichardt](https://thepracticalsysadmin.com/lint-your-dockerfiles-with-hadolint/): +From the blog by [Josh Reichardt](https://thepracticalsysadmin.com/lint-your-dockerfiles-with-hadolint/): > If you haven’t already gotten in to the habit of linting your Dockerfiles you should. Code linting is a common practice in software development which helps find, identify and eliminate issues and bugs before they are ever able to become a problem. One of the main benefits of linting your code is that it helps identify and eliminate nasty little bugs before they ever have a chance to become a problem. +From the blog by [Jamie Phillips](https://www.phillipsj.net/posts/hadolint-linting-your-dockerfile/) +> Linters are commonly used in development to help teams detect programmatic and stylistic errors. Hadolint is a linter created for Dockerfiles using Haskell. This tool validates against the best practices outlined by Docker and takes a neat approach to parse the Dockerfile that you should checkout. It supports all major platforms, and this tutorial will be leveraging the container to perform the linting on an example Dockerfile.
From 356c3133353eb4a255ada78838f08ba2ac5d9fac Mon Sep 17 00:00:00 2001 From: sagirk Date: Tue, 7 Jul 2020 23:05:20 +0530 Subject: [PATCH 0333/1795] announcement: Move Sagir to SC Emeritus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I've been on a hiatus from the project for a while, and don't foresee being able to come out of it soon-ish, owing to various personal reasons. Till such a time as I'm unable to contribute actively, it's best that I go emeritus and make space for new folks. Thanks for having me aboard the rocketship! Ya'll have been a wonderful team and a joy to work with. Sincere gratitude from the depth of my heart to all the steering committee members, collaborators and contributors! 🙏 Until next, Here's to the next 50,000 stars! 🌟 --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 905bfa489..36d692275 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ function someFunction() { } // Avoid -function someFunction() +function someFunction() { // code block } @@ -1132,14 +1132,16 @@ Full Stack Developer & Site Reliability Engineer based in New Zealand, intereste
+### Steering Committee Emeriti + [Sagir Khan](https://github.com/sagirk) - + -Deep specialist in JavaScript and its ecosystem — React, Node.js, MongoDB, pretty much anything that involves using JavaScript/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation, collaborating on the Community Committee's Website Redesign Initiative. +Deep specialist in JavaScript and its ecosystem — React, Node.js, TypeScript, GraphQL, MongoDB, pretty much anything that involves JS/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation.
@@ -1149,11 +1151,11 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: |:--------------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | -### Past collaborators +### Collaborator Emeriti | | | :-------------------------------------------------------------------------------------------------------------------------: | From a253c1f9c84c12089ef87403e6c7bfd8d0eea36a Mon Sep 17 00:00:00 2001 From: collierrgbsitisfise Date: Sun, 12 Jul 2020 22:25:27 +0300 Subject: [PATCH 0334/1795] revert formating changes --- sections/errorhandling/useonlythebuiltinerror.russian.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/useonlythebuiltinerror.russian.md b/sections/errorhandling/useonlythebuiltinerror.russian.md index dfb32d460..ca6bb8277 100644 --- a/sections/errorhandling/useonlythebuiltinerror.russian.md +++ b/sections/errorhandling/useonlythebuiltinerror.russian.md @@ -2,7 +2,7 @@ ### Объяснение в один абзац -Всепозволяющая природа JavaScript наряду с его разнообразием параметров потока кода (например, EventEmitter, Callbacks, Promises и т.д.) приводит к значительному расхождению в том, как разработчики выдают ошибки - некоторые используют строки, другие определяют свои собственные пользовательские типы. Использование встроенного объекта Error в Node.js помогает сохранить единообразие в вашем коде, а с помощью сторонних библиотек он также сохраняет важную информацию, такую ​​как StackTrace. При возникновении исключения обычно рекомендуется заполнить его дополнительными контекстными свойствами, такими как имя ошибки и связанный код ошибки HTTP. Чтобы добиться этого единообразия и практики, рассмотрите возможность расширения объекта Error дополнительными свойствами, см. пример кода ниже. +Гибкая природа JavaScript наряду с его разнообразием параметров потока кода (например, EventEmitter, Callbacks, Promises и т.д.) приводит к значительному расхождению в том, как разработчики выдают ошибки - некоторые используют строки, другие определяют свои собственные пользовательские типы. Использование встроенного объекта Error в Node.js помогает сохранить единообразие в вашем коде, а с помощью сторонних библиотек он также сохраняет важную информацию, такую ​​как StackTrace. При возникновении исключения обычно рекомендуется заполнить его дополнительными контекстными свойствами, такими как имя ошибки и связанный код ошибки HTTP. Чтобы добиться этого единообразия и практики, рассмотрите возможность расширения объекта Error дополнительными свойствами, см. пример кода ниже. ### Пример кода - делай все правильно From 36ac579676fb788bce16e2fba9bd015a2194cceb Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Sat, 18 Jul 2020 19:52:56 +0200 Subject: [PATCH 0335/1795] feat(docker): add multi-stage builds and smaller base image bullet points to README --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2d87bcd65..9fa206c08 100644 --- a/README.md +++ b/README.md @@ -1151,13 +1151,14 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.8. Use multistage builds +## ![✔] 8.8. Use multi-stage builds -**TL;DR:** +**TL;DR:** A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during +build, while the runtime environment contains only what's necessary. -**Otherwise:** +**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. -🔗 [**Read More: Use multistage builds**](/sections/docker/file.md) +🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md)


@@ -1171,13 +1172,14 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.10. Prefer smaller images +## ![✔] 8.10. Prefer smaller Docker base images -**TL;DR:** +**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Alpine Linux variants, +mitigates this issue. -**Otherwise:** +**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors, and more resources are consumed. -🔗 [**Read More: Prefer smaller images**](/sections/docker/file.md) +🔗 [**Read More: Prefer smaller images**](/sections/docker/smaller_base_images.md)


From 54171d0b5ac271f0554e55b9ea1db931802aaed9 Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Sat, 18 Jul 2020 19:53:09 +0200 Subject: [PATCH 0336/1795] feat(docker): draft multi-stage builds --- sections/docker/multi_stage_builds.md | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 sections/docker/multi_stage_builds.md diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md new file mode 100644 index 000000000..6864dabd6 --- /dev/null +++ b/sections/docker/multi_stage_builds.md @@ -0,0 +1,75 @@ +# Use multi-stage builds + +### One Paragraph Explainer + +Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. +Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes, you'll need to +include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. +This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime, such as API Keys and secrets +used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). + +### Example + +Let's imagine the following directory structure + +``` +- Dockerfile +- src/ + - index.ts +- package.json +- yarn.lock +- .dockerignore +- docs/ + - README.md +``` + +Your .dockerignore will already filter out files that won't be needed for building and running your application. + +``` +# Don't copy in existing node_modules, we'll fetch our own +node_modules + +# Docs are large, we don't need them in our Docker image +docs +``` + +Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image, +and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage, +and then install production dependencies. + +```dockerfile +# Start with fully-featured Node.js base image +FROM node:14.4.0 AS build + +USER node +WORKDIR /home/node/app + +# Copy dependency information and install all dependencies +COPY --chown=node:node package.json yarn.lock ./ + +RUN yarn + +# Copy source code (and all other relevant files) +COPY --chown=node:node src ./src + +# Build code +RUN yarn build + +# Run-time stage +FROM node:14.4.0-alpine + +# Set non-root user and expose port 8080 +USER node +EXPOSE 8080 + +WORKDIR /home/node/app + +# Copy dependency information and install production-only dependencies +COPY --chown=node:node package.json yarn.lock ./ +RUN yarn install --production + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist ./dist + +CMD [ "node", "dist/app.js" ] +``` \ No newline at end of file From fdb94ad44b020852a39fc88fbd8f76a999accbf2 Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Sat, 18 Jul 2020 20:00:32 +0200 Subject: [PATCH 0337/1795] feat(docker): prefer smaller docker base images --- sections/docker/smaller_base_images.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 sections/docker/smaller_base_images.md diff --git a/sections/docker/smaller_base_images.md b/sections/docker/smaller_base_images.md new file mode 100644 index 000000000..d27e120b2 --- /dev/null +++ b/sections/docker/smaller_base_images.md @@ -0,0 +1,13 @@ +# Prefer smaller Docker base images + +Large Docker images can lead to higher exposure to vulnerabilities and increased resource consumption. Often, you don't need certain packages installed at runtime that are needed for building. +Pulling and storing larger images will become more expensive at scale, when dealing with larger images. Minimal images may not come with common libraries needed for building native modules or packages +useful for debugging (e.g. curl) pre-installed, by design. Using the Alpine Linux variants of images, can lead to a reduced footprint in terms of resources used and the amount of attack vectors +present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version. + +### Blog Quote: "If you want to shrink your Docker images, have your services start faster and be more secure then try Alpine out." + +From [Nick Janetakis' blog](https://nickjanetakis.com/blog/the-3-biggest-wins-when-using-alpine-as-a-base-docker-image) + +> It’s no secret by now that Docker is heavily using Alpine as a base image for official Docker images. This movement started near the beginning of 2016. [...] + When pulling down new Docker images onto a fresh server, you can expect the initial pull to be quite a bit faster on Alpine. The slower your network is, the bigger the difference it will be. [...] Another perk of being much smaller in size is that the surface area to be attacked is much less. When there’s not a lot of packages and libraries on your system, there’s very little that can go wrong. \ No newline at end of file From 92d530e4faf547ce8a6acde9e138b5e2193bd1fd Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 15:37:07 +0300 Subject: [PATCH 0338/1795] Per PR comments --- README.md | 2 +- sections/docker/avoid-build-time-secrets.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6300589a6..73d7218f5 100644 --- a/README.md +++ b/README.md @@ -1193,7 +1193,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.12. Clean-out build-time secrets, avoid secrets in args -**TL;DR:** Avoid secrets leaking from the docker build environment. A docker image is typically shared in multiple environment, like CI and a registry, that are not as sanitized as production. A typical example is a npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like .npmrc and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces +**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment, like CI and a registry, that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like .npmrc and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces **Otherwise:** Everyone with access to the CI and docker registry will also get as a bonus access to some precious organization secrets diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md index e9a34bc81..460b18384 100644 --- a/sections/docker/avoid-build-time-secrets.md +++ b/sections/docker/avoid-build-time-secrets.md @@ -4,7 +4,8 @@ ### One Paragraph Explainer -A docker image is not just a bunch of files rather constitutes multiple layers that reveal also what happened during the build time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however in fact this token can now be fetched from the developers machine Docker history, from the Docker registry and the CI. An attacker who get access to that token, is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not prevent the secrets file from appearing in the local docker history but considered as as secured enough in some organizations. + +A Docker image isn't just a bunch of files but rather multiple layers revealing what happened during build-time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however in fact this token can now be fetched from the developers machine Docker history, from the Docker registry and the CI. An attacker who get access to that token, is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not ship the secrets with the images but will appear in the local Docker history - This is typically considered as secured enough for most organizations.

From 942730e2af7488132b52de706beaca43eb38e63c Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 16:02:20 +0300 Subject: [PATCH 0339/1795] Fixes per PR comments --- sections/docker/install-for-production.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md index 6e33a4d4b..a597e5e4d 100644 --- a/sections/docker/install-for-production.md +++ b/sections/docker/install-for-production.md @@ -38,6 +38,7 @@ RUN npm ci --production && npm clean cache --force FROM node:12-slim AS build WORKDIR /usr/src/app COPY package.json package-lock.json ./ +# Two mistakes below: Installing dev dependencies, not deleting the cache after npm install RUN npm install # The rest comes here From 765b84d00008a3871f93927ef264c5f56c1bfd63 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 16:16:15 +0300 Subject: [PATCH 0340/1795] Fixes --- sections/docker/generic-tips.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sections/docker/generic-tips.md b/sections/docker/generic-tips.md index de8836ba4..54570d5c4 100644 --- a/sections/docker/generic-tips.md +++ b/sections/docker/generic-tips.md @@ -10,15 +10,15 @@ This common Docker guidelines section contains best practices that are standardi ## ![✔] Avoid updating the base OS -**TL;DR:** Updating the local binaries during build (e.g. apt-get update) creates inconsistent images every time it runs and also demands high privilige. Instead use base images that are relatively updated +**TL;DR:** Updating the local binaries during build (e.g. apt-get update) creates inconsistent images every time it runs and also demands elevated privileges. Instead use base images that are updated frequently -## ![✔] Tag images using labels +## ![✔] Classify images using labels -**TL;DR:** Providing meta-data for each image might help Ops professional treat it adequately. For example, include the maintainer name, build date and other information that might prove useful when someone need to reason about an image +**TL;DR:** Providing metadata for each image might help Ops professionals treat it adequately. For example, include the maintainer name, build date and other information that might prove useful when someone needs to reason about an image ## ![✔] Use unprivileged containers -**TL;DR:** Privileged container have the same permissions and capabilities as the root user over the host machine. This is rarely needed and as a rule of thumb should be avoided +**TL;DR:** Privileged container have the same permissions and capabilities as the root user over the host machine. This is rarely needed and as a rule of thumb one should use the 'node' user that is created within official Node images ## ![✔] Inspect and verify the final result From 4cd5be2345b15a3610ced532b12a928846b27eb7 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 16:24:37 +0300 Subject: [PATCH 0341/1795] fixes --- sections/docker/docker-ignore.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md index 21a97a146..4b0f31f94 100644 --- a/sections/docker/docker-ignore.md +++ b/sections/docker/docker-ignore.md @@ -24,6 +24,7 @@ The Docker build command copies the local files into the build context environme **/.env **/.editorconfig **/.aws +**/dist ```
From d0e16544dd4fbb1caa40970037ae5aa44af4f9db Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 16:57:43 +0300 Subject: [PATCH 0342/1795] Fixed per the PR comments --- sections/docker/docker-ignore.md | 49 +++++++++++++++++++ .../docker/restart-and-replicate-processes.md | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 sections/docker/docker-ignore.md diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md new file mode 100644 index 000000000..4b0f31f94 --- /dev/null +++ b/sections/docker/docker-ignore.md @@ -0,0 +1,49 @@ +# Use .dockerignore to prevent leaking secrets + +

+ +### One Paragraph Explainer + +The Docker build command copies the local files into the build context environment over a virtual network. Be careful - development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world, the Dockerfile should be explicit about what is being copied. On top of this, include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the build can better utilize the cache and achieve better performance + +

+ +### Code Example – A good default .dockerignore for Node.js + +
+.dockerignore + +``` +**/node_modules/ +**/.git +**/README.md +**/LICENSE +**/.vscode +**/npm-debug.log +**/coverage +**/.env +**/.editorconfig +**/.aws +**/dist +``` + +
+ +

+ +### Code Example Anti-Pattern – Recursive copy of all files + +
+Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +# The next line copies everything +COPY . . + +# The rest comes here + +``` + +
diff --git a/sections/docker/restart-and-replicate-processes.md b/sections/docker/restart-and-replicate-processes.md index d9ed157d1..0f03c1d90 100644 --- a/sections/docker/restart-and-replicate-processes.md +++ b/sections/docker/restart-and-replicate-processes.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and can conclude many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that, some may tempt to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side, When using local tools for restarting the process, the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone. +Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and take into account many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that, some may tempt to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side, When using local tools for restarting the process, the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone.

From 3890a679edc142c963068e70fe808b09fd89c088 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 17:07:26 +0300 Subject: [PATCH 0343/1795] Fixed per the PR comments --- sections/docker/memory-limit.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md index 583891519..c38256f5d 100644 --- a/sections/docker/memory-limit.md +++ b/sections/docker/memory-limit.md @@ -71,6 +71,14 @@ spec: ### Kubernetes documentation: "If you do not specify a memory limit" -From [K8S documentation](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/?fbclid=IwAR2gfRL-3UB2WIbxJNSl3jNdlEJF3rAU5WG049yNorLOVSVngmRY1xhrbqg#if-you-do-not-specify-a-memory-limit) +From [K8S documentation](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/) > The Container has no upper bound on the amount of memory it uses. The Container could use all of the memory available on the Node where it is running which in turn could invoke the OOM Killer. Further, in case of an OOM Kill, a container with no resource limits will have a greater chance of being killed. + +

+ +### Docker documentation: "it throws an OOME and starts killing processes " + +From [Docker official docs](https://docs.docker.com/config/containers/resource_constraints/) + +> It is important not to allow a running container to consume too much of the host machine’s memory. On Linux hosts, if the kernel detects that there is not enough memory to perform important system functions, it throws an OOME, or Out Of Memory Exception, and starts killing processes to free up memory. From 17b3e7497eeaab6c1c5bd3d29e005fd3d27cfa5f Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 17:14:57 +0300 Subject: [PATCH 0344/1795] Fixed per the PR comments --- README.md | 4 ++-- sections/docker/memory-limit.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1dfd8ea5e..36c61fef4 100644 --- a/README.md +++ b/README.md @@ -1131,9 +1131,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.6. Set memory limits using Docker only +## ![✔] 8.6. Set memory limits using Docker -**TL;DR:** Configure a memory limit using Docker, not using V8. Use Docker command flag 'run --memory' or set the right values within the platform that runs Docker. By doing this, the runtime will be capable of making better decisions on when to scale, prevent one citizen from starving others, drive thoughtful crash decisions (e.g., Docker can allow slight burst deviations) and in-overall it's always better to move HW decisions to the OPS court +**TL;DR:** Always configure a memory limit using Docker, optionally set also the v8 limits. Practically, use the Docker flag 'run --memory' or set the right values within the platform that runs Docker. By doing this, the runtime will be capable of making better decisions on when to scale, prevent one citizen from starving others, drive thoughtful crash decisions (e.g., Docker can allow slight burst deviations) and in-overall it's always better to move HW decisions to the OPS court **Otherwise:** When setting limits using V8 --max-old-space-size the Docker runtime won't be aware of its capacity limits and will have to blindly place it in an instance that might not have the right size diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md index c38256f5d..264dc89b2 100644 --- a/sections/docker/memory-limit.md +++ b/sections/docker/memory-limit.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime. There is confusion around which of those to set, some configure both. The better option would be to set the limit on the Docker command or the Docker runtime (e.g. Kubernetes) as is has a much wider perspective for making the right decisions. First, given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Docker also measures the actually used memory and not the allocated part so it can get Killed only when really needed. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. On the contrary, when setting the limit on V8 - each of the described scenarios will not be supported. +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime. There is confusion around which of those to set: Ensure to always configure the Docker runtime limits as is has a much wider perspective for making the right decisions. First, given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Docker also measures the actually used memory and not the allocated part (unlike v8 --max-old-space-size) so it can get Killed only when really needed. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap.

From 34e45441eb9c2462e153ad1d9a64feb0371545d7 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 26 Jul 2020 17:19:42 +0300 Subject: [PATCH 0345/1795] Fixed per the PR comments --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23f01a5c2..b61f99549 100644 --- a/README.md +++ b/README.md @@ -1083,7 +1083,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1. Clean NODE_MODULE cache -**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies packages to speed future installs since there won't be any further installs - A docker image is created only once. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved +**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off **Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used From b1137173943af186d2f47a203b54e47aa0bf2c78 Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Mon, 27 Jul 2020 19:33:03 +0200 Subject: [PATCH 0346/1795] feat(docker): address feedback --- README.md | 7 +++---- sections/docker/multi_stage_builds.md | 12 ++++-------- sections/docker/smaller_base_images.md | 3 ++- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9fa206c08..4eca7aac9 100644 --- a/README.md +++ b/README.md @@ -1151,10 +1151,10 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.8. Use multi-stage builds +## ![✔] 8.8. Use multi-stage builds for leaner and more secure Docker images **TL;DR:** A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during -build, while the runtime environment contains only what's necessary. +build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to reduce overhead in size and vulnerabilities. **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. @@ -1174,8 +1174,7 @@ build, while the runtime environment contains only what's necessary. ## ![✔] 8.10. Prefer smaller Docker base images -**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Alpine Linux variants, -mitigates this issue. +**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Alpine Linux variants, mitigates this issue. **Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors, and more resources are consumed. diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md index 6864dabd6..ae50f5d97 100644 --- a/sections/docker/multi_stage_builds.md +++ b/sections/docker/multi_stage_builds.md @@ -2,11 +2,7 @@ ### One Paragraph Explainer -Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. -Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes, you'll need to -include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. -This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime, such as API Keys and secrets -used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). +Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes, you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime, such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). ### Example @@ -23,7 +19,7 @@ Let's imagine the following directory structure - README.md ``` -Your .dockerignore will already filter out files that won't be needed for building and running your application. +Your [.dockerignore](/sections/docker/dockerignore.md) will already filter out files that won't be needed for building and running your application. ``` # Don't copy in existing node_modules, we'll fetch our own @@ -47,7 +43,7 @@ WORKDIR /home/node/app # Copy dependency information and install all dependencies COPY --chown=node:node package.json yarn.lock ./ -RUN yarn +RUN yarn install --frozen-lockfile # Copy source code (and all other relevant files) COPY --chown=node:node src ./src @@ -66,7 +62,7 @@ WORKDIR /home/node/app # Copy dependency information and install production-only dependencies COPY --chown=node:node package.json yarn.lock ./ -RUN yarn install --production +RUN yarn install --frozen-lockfile --production # Copy results from previous stage COPY --chown=node:node --from=build /home/node/app/dist ./dist diff --git a/sections/docker/smaller_base_images.md b/sections/docker/smaller_base_images.md index d27e120b2..d1fb358dc 100644 --- a/sections/docker/smaller_base_images.md +++ b/sections/docker/smaller_base_images.md @@ -3,7 +3,8 @@ Large Docker images can lead to higher exposure to vulnerabilities and increased resource consumption. Often, you don't need certain packages installed at runtime that are needed for building. Pulling and storing larger images will become more expensive at scale, when dealing with larger images. Minimal images may not come with common libraries needed for building native modules or packages useful for debugging (e.g. curl) pre-installed, by design. Using the Alpine Linux variants of images, can lead to a reduced footprint in terms of resources used and the amount of attack vectors -present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version. +present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version, which is almost 10x smaller. A Slim variant based on Debian, which is only 38MB in size and contains the minimal +packages needed to run Node.js, is also a great choice. ### Blog Quote: "If you want to shrink your Docker images, have your services start faster and be more secure then try Alpine out." From 54e206cde09af4898a8836d6a49e74d5088d3ab0 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 22 Jul 2020 11:38:17 +0200 Subject: [PATCH 0347/1795] Add BP about bootstraping using node instead of npm --- README.md | 7 +-- sections/docker/bootstrap-using-node.md | 62 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 sections/docker/bootstrap-using-node.md diff --git a/README.md b/README.md index cdc071fc9..ffc47c45e 100644 --- a/README.md +++ b/README.md @@ -1094,11 +1094,12 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.2. Bootstrap the code using 'node' command, avoid 'npm run' scripts -**TL;DR:** +**TL;DR:** use `CMD ['node','server.js']` to start your app. This prevents problems with child-process, signal handling and avoid creating unnecessary processes. -**Otherwise:** -🔗 [**Read More: Clean npm cache**](/sections/docker/file.md) +**Otherwise:** You'll have hard shutdowns, possibly losing current requests and/or data + +[**Read More: Bootstrap container using node command, avoid npm start**](/sections/docker/bootstrap-using-node.md)


diff --git a/sections/docker/bootstrap-using-node.md b/sections/docker/bootstrap-using-node.md new file mode 100644 index 000000000..e636471d3 --- /dev/null +++ b/sections/docker/bootstrap-using-node.md @@ -0,0 +1,62 @@ +# Bootstrap container using node command instead of npm + +## One paragraph explainer + +We are used to see code examples where folks start their app using `CMD 'npm start'`. This is a bad practice. The `npm` binary will not forward signals to your app which prevents graceful shutdown (see #716). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. `npm start` also results in having an extra process for no benefit. To start you app use `CMD ['node','server.js']`. + +### Code example + +```dockerfile + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +CMD ['node', 'server.js'] +``` + +### Antipatterns + +Using npm start +```dockerfile + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# don’t do that! +CMD “npm start” +``` + +Using node in a single string will start a bash/ash shell process to execute your command. That is almost the same as using `npm` + +```dockerfile + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# don’t do that, it will start bash +CMD "node server.js" +``` + +Starting with npm, here’s the process tree: +``` +$ ps falx + UID PID PPID COMMAND + 0 1 0 npm + 0 16 1 sh -c node server.js + 0 17 16 \_ node server.js +``` +There is no advantage to those two extra process. + +Sources: + + +https://maximorlov.com/process-signals-inside-docker-containers/ + + +https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#handling-kernel-signals From 01ff3a75c8d3df47ccc596eeaeaa43b72051a63f Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 29 Jul 2020 09:02:28 +0200 Subject: [PATCH 0348/1795] PR remarks --- README.md | 2 +- sections/docker/bootstrap-using-node.md | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ffc47c45e..229990e41 100644 --- a/README.md +++ b/README.md @@ -1097,7 +1097,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **TL;DR:** use `CMD ['node','server.js']` to start your app. This prevents problems with child-process, signal handling and avoid creating unnecessary processes. -**Otherwise:** You'll have hard shutdowns, possibly losing current requests and/or data +**Otherwise:** When no signals are passed in you'll have hard shutdowns, possibly losing current requests and/or data [**Read More: Bootstrap container using node command, avoid npm start**](/sections/docker/bootstrap-using-node.md) diff --git a/sections/docker/bootstrap-using-node.md b/sections/docker/bootstrap-using-node.md index e636471d3..be2e1d9ba 100644 --- a/sections/docker/bootstrap-using-node.md +++ b/sections/docker/bootstrap-using-node.md @@ -2,18 +2,26 @@ ## One paragraph explainer -We are used to see code examples where folks start their app using `CMD 'npm start'`. This is a bad practice. The `npm` binary will not forward signals to your app which prevents graceful shutdown (see #716). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. `npm start` also results in having an extra process for no benefit. To start you app use `CMD ['node','server.js']`. +We are used to see code examples where folks start their app using `CMD 'npm start'`. This is a bad practice. The `npm` binary will not forward signals to your app which prevents graceful shutdown (see [/sections/docker/graceful-shutdown.md]). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. `npm start` also results in having an extra process for no benefit. To start you app use `CMD ['node','server.js']`. If your app spawns child-processes also use `TINI` as an entrypoint. ### Code example ```dockerfile FROM node:12-slim AS build + +# Add Tini if using child-processes +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini +RUN chmod +x /tini + WORKDIR /usr/src/app COPY package.json package-lock.json ./ RUN npm ci --production && npm clean cache --force -CMD ['node', 'server.js'] +ENTRYPOINT ["/tini", "--"] + +CMD ["node", "server.js"] ``` ### Antipatterns @@ -27,7 +35,7 @@ COPY package.json package-lock.json ./ RUN npm ci --production && npm clean cache --force # don’t do that! -CMD “npm start” +CMD "npm start" ``` Using node in a single string will start a bash/ash shell process to execute your command. That is almost the same as using `npm` From 351d4003c06ff779410521621359469fccbaa069 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Fri, 24 Jul 2020 20:06:49 +0200 Subject: [PATCH 0349/1795] Add section about docker caching --- assets/images/docker_layers_schema.png | Bin 0 -> 176222 bytes .../docker/Caching-for-shorter-build-time.md | 103 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 assets/images/docker_layers_schema.png create mode 100644 sections/docker/Caching-for-shorter-build-time.md diff --git a/assets/images/docker_layers_schema.png b/assets/images/docker_layers_schema.png new file mode 100644 index 0000000000000000000000000000000000000000..16084e21ab9113a9324996d3f45c2140889a01d1 GIT binary patch literal 176222 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

RTC1Y^};6<=3 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 Date: Fri, 24 Jul 2020 20:15:19 +0200 Subject: [PATCH 0350/1795] Be more specific about command caching and cache invalidation --- .../docker/Caching-for-shorter-build-time.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sections/docker/Caching-for-shorter-build-time.md b/sections/docker/Caching-for-shorter-build-time.md index 7518274b2..c2f5dd40e 100644 --- a/sections/docker/Caching-for-shorter-build-time.md +++ b/sections/docker/Caching-for-shorter-build-time.md @@ -2,10 +2,11 @@ ## One paragraph explainer -Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions and files used are identical. +Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identicals or in the case of a `COPY` or `ADD` files used are identical. It is important to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. +⚠️ If the cache can't be used for a particular layer, all the subsquent layers will be invalidated too. That's why order is important. ![Docker layers](/assests/images/docker_layers_schema.png) @@ -13,6 +14,20 @@ Rebuilding a whole docker image from cache can be nearly instantaneous if done c ### Rules +#### Avoid LABEL that change all the time + +If you have a label containing the build number at the top of your Dockerfile, the cache will be invalidated at every build + +```Dockerfile +#Beginning of the file +FROM node:10.22.0-alpine3.11 as builder + +# Don't do that here! +LABEL build_number="483" + +#... Rest of the Dockerfile +``` + #### Have a good .dockerignore file [**See: On the importance of docker ignore**](/sections/docker/docker-ignore.md) @@ -100,4 +115,6 @@ RUN npm prune --production CMD ["node", "dist/server.js"] ``` +## Useful links +Docker docks: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache From 0a2d40c57c93c19a8bcd37d312d37292a5381d10 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 29 Jul 2020 09:19:21 +0200 Subject: [PATCH 0351/1795] Refining after PR comments --- sections/docker/Caching-for-shorter-build-time.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/sections/docker/Caching-for-shorter-build-time.md b/sections/docker/Caching-for-shorter-build-time.md index c2f5dd40e..8376966f9 100644 --- a/sections/docker/Caching-for-shorter-build-time.md +++ b/sections/docker/Caching-for-shorter-build-time.md @@ -2,11 +2,7 @@ ## One paragraph explainer -Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identicals or in the case of a `COPY` or `ADD` files used are identical. -It is important to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. -Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. - -⚠️ If the cache can't be used for a particular layer, all the subsquent layers will be invalidated too. That's why order is important. +Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identicals or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer, all the subsquent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes everytime you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. ![Docker layers](/assests/images/docker_layers_schema.png) @@ -36,8 +32,8 @@ The docker ignore avoids copying files that could bust our cache logic, like tes #### Install "system" packages first -If you need to install packages using `apt`,`yum`,`apk` or the likes, this should be one of the first instructions. You don't want to reinstall make,gcc or g++ every time you build your node app. -Do not install package only for convenience. This is a production app, no need to get +It is recommended to create a base docker image that has all the system packages you use. If you **really** need to install packages using `apt`,`yum`,`apk` or the likes, this should be one of the first instructions. You don't want to reinstall make,gcc or g++ every time you build your node app. +**Do not install package only for convenience, this is a production app.** #### First, only ADD your package.json and your lockfile @@ -46,7 +42,6 @@ COPY "package.json" "package-lock.json" "./" RUN npm ci ``` - The lockfile and the package.json change less often. Copying them first will keep the `npm install` step in the cache, this saves precious time. ### Then copy your files and run build step (if needed) From 4c4b652c9fa2140938b3abae47edf881830f49be Mon Sep 17 00:00:00 2001 From: Skurishin Vladislav Date: Tue, 4 Aug 2020 21:28:28 +0300 Subject: [PATCH 0352/1795] fix typos --- README.russian.md | 6 +++--- sections/errorhandling/asyncerrorhandling.russian.md | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.russian.md b/README.russian.md index 8dc472e98..9198f4b27 100644 --- a/README.russian.md +++ b/README.russian.md @@ -90,9 +90,9 @@ ## ![✔] 1.4 Разделяйте Express "приложение" и "сервер" -**TL;DR:** Избегайте неприятной привычки определять все приложение [Express] (https://expressjs.com/) в одном огромном файле -- разделите определение "Экспресс" как минимум на два файла: декларация API (app.js) и сетевые задачи (www). Для еще лучшей структуры локализуйте объявление API в компонентах. +**TL;DR:** Избегайте неприятной привычки определять все приложение [Express](https://expressjs.com/) в одном огромном файле -- разделите определение "Экспресс" как минимум на два файла: декларация API (app.js) и сетевые задачи (www). Для еще лучшей структуры локализуйте объявление API в компонентах. -**Иначе:** Ваш API будет доступен для тестирования только через HTTP-вызовы (медленнее и намного сложнее создавать отчеты о покрытии). Скорее всего, не составит большого труда хранить сотни строк кода в одном файле. +**Иначе:** Ваш API будет доступен для тестирования только через HTTP-вызовы (медленнее и намного сложнее создавать отчеты о покрытии). Скорее всего, вы не будете испытывать огромное удовольствие, если будете хранить сотни строк кода в одном файле. 🔗 [**Подробнее: Разделяйте Express "приложение" и "сервер"**](/sections/projectstructre/separateexpress.russian.md) @@ -102,7 +102,7 @@ **TL;DR:** Идеальная и безупречная конфигурация должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) -**Иначе:** Невыполнение каких-либо требований к конфигурации приведет к срывам в работе разработчиков или devops командой. Вероятно, и тех и других. +**Иначе:** Невыполнение каких-либо требований к конфигурации приведет к срывам в работе разработчиков или devops-команды. А вероятно, и тех и других. 🔗 [**Подробнее: Используйте конфигурацию с учетом среды, безопасную и иерархическую конфигурацию**](/sections/projectstructre/configguide.russian.md) diff --git a/sections/errorhandling/asyncerrorhandling.russian.md b/sections/errorhandling/asyncerrorhandling.russian.md index 5e0c9d5ba..5cf3ad2c4 100644 --- a/sections/errorhandling/asyncerrorhandling.russian.md +++ b/sections/errorhandling/asyncerrorhandling.russian.md @@ -2,7 +2,6 @@ ### Объяснение в один абзац -Обратные вызовы плохо масштабируются, так как большинство программистов не знакомы с ними. Они заставляют проверять ошибки повсюду, иметь дело с неприятной вложенностью кода и затрудняют анализ потока кода. Библиотеки обещаний (promise), такие как BlueBird, async и Q, упаковывают стандартный стиль кода, используя RETURN и THROW для управления потоком программы. В частности, они поддерживают наилучший стиль обработки ошибок try-catch, который позволяет освободить основной код от обработки ошибок в каждой функции. Обратные вызовы плохо масштабируются, так как большинство программистов не знакомы с ними. Они заставляют проверять ошибки повсюду, иметь дело с неприятной вложенностью кода и затрудняют анализ потока кода. Библиотеки обещаний (promise), такие как BlueBird, async и Q, упаковывают стандартный стиль кода, используя RETURN и THROW для управления потоком программы. В частности, они поддерживают наилучший стиль обработки ошибок try-catch, который позволяет освободить основной код от обработки ошибок в каждой функции. ### Пример кода – использование обещаний для отлова ошибок From 0a8a214a54b38cebe57eace5dc5dd7c2682f5bf4 Mon Sep 17 00:00:00 2001 From: Skurishin Vladislav Date: Tue, 4 Aug 2020 21:38:11 +0300 Subject: [PATCH 0353/1795] fix typo with "flexible" word --- sections/errorhandling/useonlythebuiltinerror.russian.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/errorhandling/useonlythebuiltinerror.russian.md b/sections/errorhandling/useonlythebuiltinerror.russian.md index 99dfeec58..4688bbcfb 100644 --- a/sections/errorhandling/useonlythebuiltinerror.russian.md +++ b/sections/errorhandling/useonlythebuiltinerror.russian.md @@ -2,7 +2,7 @@ ### Объяснение в один абзац -Позволяющая природа JavaScript наряду с его разнообразием вариантов потока кода (например, EventEmitter, Callbacks, Promises и т.д.) приводит к значительному расхождению в том, как разработчики выдают ошибки - некоторые используют строки, другие определяют свои собственные пользовательские типы. Использование встроенного объекта Error в Node.js помогает сохранить единообразие в вашем коде, а с помощью сторонних библиотек он также сохраняет важную информацию, такую ​​как StackTrace. При возникновении исключения обычно рекомендуется заполнить его дополнительными контекстными свойствами, такими как имя ошибки и связанный код ошибки HTTP. Чтобы добиться этого единообразия и практики, рассмотрите возможность расширения объекта Error дополнительными свойствами, см. пример кода ниже. +Гибкая природа JavaScript наряду с его разнообразием вариантов потока кода (например, EventEmitter, Callbacks, Promises и т.д.) приводит к значительному расхождению в том, как разработчики выдают ошибки - некоторые используют строки, другие определяют свои собственные пользовательские типы. Использование встроенного объекта Error в Node.js помогает сохранить единообразие в вашем коде, а с помощью сторонних библиотек он также сохраняет важную информацию, такую ​​как StackTrace. При возникновении исключения обычно рекомендуется заполнить его дополнительными контекстными свойствами, такими как имя ошибки и связанный код ошибки HTTP. Чтобы добиться этого единообразия и практики, рассмотрите возможность расширения объекта Error дополнительными свойствами, см. пример кода ниже. ### Пример кода - делай все правильно From fb0da25efd8c9ffc6d2d56368bc9232ed1e77df7 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 16 Aug 2020 16:45:18 +0300 Subject: [PATCH 0354/1795] Sorting the bullets --- README.md | 122 +++++++++------------- sections/docker/install-for-production.md | 55 ++++++++++ sections/docker/scan-images.md | 30 ++++++ temp.md | 38 +++++++ 4 files changed, 174 insertions(+), 71 deletions(-) create mode 100644 sections/docker/install-for-production.md create mode 100644 sections/docker/scan-images.md create mode 100644 temp.md diff --git a/README.md b/README.md index 2feafb897..434f77de0 100644 --- a/README.md +++ b/README.md @@ -736,7 +736,7 @@ All statements above will return false if used with `===` **TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to do a clean install of your dependencies matching package.json and package-lock.json. -**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**Otherwise:****** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code 🔗 [**Read More: Use npm ci**](/sections/production/installpackageswithnpmci.md) @@ -1077,63 +1077,73 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E # `7. Draft: Docker Best Practices` -## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/682) +🏅 Many thanks to [Bret Fisher](https://github.com/BretFisher) from whom we learned many of the following practices

-## ![✔] 8.1. Clean NODE_MODULE cache - -**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off +## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images +**TL;DR:** A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to reduce overhead in size and vulnerabilities. -**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used +**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. -🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) +🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md)


-## ![✔] 8.2. Bootstrap the code using 'node' command, avoid 'npm run' scripts - -**TL;DR:** use `CMD ['node','server.js']` to start your app. This prevents problems with child-process, signal handling and avoid creating unnecessary processes. +## ![✔] 8.2. Bootstrap using 'node' command, avoid npm start +**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts. This prevents problems with child-process, signal handling, graceful shutdown and unnecessary processes. -**Otherwise:** When no signals are passed in you'll have hard shutdowns, possibly losing current requests and/or data +**Otherwise:** When no signals are passed in you'll have hard shutdowns, possibly losing current requests and/or data. [**Read More: Bootstrap container using node command, avoid npm start**](/sections/docker/bootstrap-using-node.md)


-## ![✔] 8.3. Install packages for production +## ![✔] 8.3. Let the Docker runtime handle replication and uptime -**TL;DR:** +**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediating process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes -**Otherwise:** +**Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance + +🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](/sections/docker/restart-and-replicate-processes.md) + +


+ +## ![✔] 8.4. Use .dockerignore to prevent leaking secrets + +**TL;DR**: Include a .dockerignore file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus, the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker + +**Otherwise**: Common personal secret files like .env, .aws and .npmrc will be shared with anybody with access to the image (e.g. Docker repository) + +🔗 [**Read More: Use .dockerignore**](/sections/docker/lint-dockerfile.md) -🔗 [**Read More: Bootstrap the code using 'node' command, avoid 'npm run' scripts**](/sections/docker/file.md)


-## ![✔] 8.4. Lint your Dockerfile +## ![✔] 8.5. Clean-up dependencies before production -**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. +**TL;DR:** Althoug DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so gurantee that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' -**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user. +**Otherwise:** Many of the infamous npm security breaches were found within development packages + +🔗 Read More: [Remove development dependencies](/sections/docker/install-for-production.md) -🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md)


-## ![✔] 8.5. Utilize caching for better build time +## ![✔] 8.6. Shutdown smartly and gracefully -**TL;DR:** +**TL;DR:** Handle the process SIGTERM event and clean-up all existing conenction and resources. This should be done while responding to ongoing reqeusts. In Dockerized runtimes, shuting down containers is not a rare event rather a frequent occurence that happen as part of routine work. Acheiving this demand some thoughful code to orchestrate few moving parts: The load balancer, keep-alive connections, the HTTP server and other resources -**Otherwise:** +**Otherwise:** Dying immediately means not responding to thousands of disappointed users -🔗 [**Read More: Utilize caching for better build time**](/sections/docker/file.md) +🔗 [**Read More: Graceful shutdown**](/sections/docker/graceful-shutdown.md)


-## ![✔] 8.6. Set memory limits using Docker +## ![✔] 8.7. Set memory limits using both Docker and v8 **TL;DR:** Always configure a memory limit using Docker, optionally set also the v8 limits. Practically, use the Docker flag 'run --memory' or set the right values within the platform that runs Docker. By doing this, the runtime will be capable of making better decisions on when to scale, prevent one citizen from starving others, drive thoughtful crash decisions (e.g., Docker can allow slight burst deviations) and in-overall it's always better to move HW decisions to the OPS court @@ -1143,7 +1153,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


-## ![✔] 8.7. Scan your image for vulnerabilities +## ![✔] 8.8. Caching **TL;DR:** @@ -1151,20 +1161,10 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E 🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/file.md) -


- -## ![✔] 8.8. Use multi-stage builds for leaner and more secure Docker images - -**TL;DR:** A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during -build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to reduce overhead in size and vulnerabilities. - -**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. - -🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md)


-## ![✔] 8.9. Understand image tags vs digests, and use the "latest" tag with caution +## ![✔] 8.9. Use explicit image reference, avoid 'latest' tag **TL;DR:** The latest tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. @@ -1184,19 +1184,10 @@ In addition, referring to an image tag means that the base image is subject to c 🔗 [**Read More: Prefer smaller images**](/sections/docker/smaller_base_images.md) -


- -## ![✔] 8.11. Graceful shutdown - -**TL;DR:** Handle the process SIGTERM event and clean-up all existing conenction and resources. This should be done while responding to ongoing reqeusts. In Dockerized runtimes, shuting down containers is not a rare event rather a frequent occurence that happen as part of routine work. Acheiving this demand some thoughful code to orchestrate few moving parts: The load balancer, keep-alive connections, the HTTP server and other resources - -**Otherwise:** Dying immediately means not responding to thousands of disappointed users - -🔗 [**Read More: Graceful shutdown**](/sections/docker/graceful-shutdown.md)


-## ![✔] 8.12. Clean-out build-time secrets, avoid secrets in args +## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args **TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment, like CI and a registry, that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like .npmrc and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces @@ -1207,37 +1198,25 @@ In addition, referring to an image tag means that the base image is subject to c


-## ![✔] 8.13. On the importance of docker ignore - -**TL;DR:** - -**Otherwise:** - -🔗 [**Read More: On the importance of docker ignore**](/sections/docker/file.md) - -


- -## ![✔] 8.14. Avoid inconsistent images +## ![✔] 8.12. Scan images for multi layers of vulnerabilities -**TL;DR:** +**TL;DR:** Besides checking code dependencies vulnerabilities, also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins -**Otherwise:** +**Otherwise:** Your code might be entirely free from vulnerabilities. However, it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications -🔗 [**Read More: Avoid inconsistent images**](/sections/docker/file.md) -


+🔗 [**Read More: Generic Docker practices**](/sections/docker/scan-images.md) -## ![✔] 8.15. Avoid process managers +## ![✔] 8.13 Clean NODE_MODULE cache -**TL;DR:** +**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off -**Otherwise:** -🔗 [**Read More: Avoid process managers**](/sections/docker/file.md) +**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used -


+🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) -## ![✔] 8.16. Generic Docker practices +## ![✔] 8.14. Generic Docker practices **TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. @@ -1246,13 +1225,14 @@ In addition, referring to an image tag means that the base image is subject to c


-## ![✔] 8.17. Let the Docker orchestrator restart and replicate processes -**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediating process managers or custom code that replicate the process (e.g., Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes +## ![✔] 8.15. Lint your Dockerfile -**Otherwise:** Container keeps crashing due to lack of resources will get restarted indifiently by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code -🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](/sections/docker/restart-and-replicate-processes.md) +**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user + +🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md)


diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md new file mode 100644 index 000000000..a597e5e4d --- /dev/null +++ b/sections/docker/install-for-production.md @@ -0,0 +1,55 @@ +# Remove development dependencies + +

+ +### One Paragraph Explainer + +Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependecies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons, the image that is finally shipped to production should be safe and minimal. Running npm install with a --production is a great start however it get even safer to run 'npm ci' that ensure a fresh install and the existence of lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, multi stage builds can help in having different sets of dependencies and finally only those for production (See dedicated bullet on multi stage builds) + +

+ +### Code Example – Installing for production + +

+ +Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# The rest comes here +``` + +
+ +

+ +### Code Example Anti-Pattern – Installing all dependencies + +
+ +Dockerfile + +``` + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +# Two mistakes below: Installing dev dependencies, not deleting the cache after npm install +RUN npm install + +# The rest comes here +``` + +
+ +

+ +### Blog Quote: "npm ci is also more strict than a regular install" + +From [npm documentation](https://docs.npmjs.com/cli/ci.html) + +> This command is similar to npm-install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment – or any situation where you want to make sure you’re doing a clean install of your dependencies. It can be significantly faster than a regular npm install by skipping certain user-oriented features. It is also more strict than a regular install, which can help catch errors or inconsistencies caused by the incrementally-installed local environments of most npm users. diff --git a/sections/docker/scan-images.md b/sections/docker/scan-images.md new file mode 100644 index 000000000..8f3411d16 --- /dev/null +++ b/sections/docker/scan-images.md @@ -0,0 +1,30 @@ +# Scan the entire image before production + +

+ +### One Paragraph Explainer + +Scanning the code for vulnerabilities is a valuable act, but it doesn't cover all the potential threats. Why? Because vulnerabilities also exist on the OS level and the app might execute those binaries like Shell, Tarball, OpenSSL. Also, vulnerable dependencies might be injected after the code scan (i.e. supply chain attacks) - hence scanning the final image just before production is in order. This idea resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the assmebled deliverable. There are 3 main scanner families: Local/CI binaries with a cached vulnerabilities DB, scanners as a service in the cloud and a niche of tools which scan during the docker build itself. The first group is the most popular and usually the fastest - Tools like [Trivvy](https://github.com/aquasecurity/trivy), [Anchore](https://github.com/anchore/anchore) and [Snyk](https://support.snyk.io/hc/en-us/articles/360003946897-Container-security-overview) are worth exploring. Most CI vendors provide a local plugin that facilitates the interaction with these scanners. It should be noted that these scanners cover a lot of ground and therefore will show findings in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed + +

+ +### Code Example – Scanning with Trivvy + +
+ +Bash + +``` +sudo apt-get install rpm +$ wget https://github.com/aquasecurity/trivy/releases/download/{TRIVY_VERSION}/trivy_{TRIVY_VERSION}_Linux-64bit.deb +$ sudo dpkg -i trivy_{TRIVY_VERSION}_Linux-64bit.deb +trivy image [YOUR_IMAGE_NAME] +``` + +
+ +

+ +### Report Example – Docker scan results (By Anchore) + +![Report examples](/assets/images/anchore-report.png "Docker scan report") diff --git a/temp.md b/temp.md new file mode 100644 index 000000000..d9cd06890 --- /dev/null +++ b/temp.md @@ -0,0 +1,38 @@ +TL;DR: Althoug DevDepencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development depdencies. Doing so gurantess that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' + +Otherwise: Many of the infamous npm security breaches were found within development packages + +🔗 Read More: Remove development dependencies + + +8.1. Clean NODE_MODULE cache + +**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off + + +**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used + +🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) + +## ![✔] 8.4. Lint your Dockerfile + +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. + +**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user. + +🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md) + +Use multistage builds + Bootstrap the code using 'node' command, avoid 'npm run' scripts + Install packages for production + Dockerignore + Graceful shutdown + Set Docker memory limits + Utilize caching for better build time + Don't use "latest", use a digest or specific tag + Prefer smaller images + Get rid of secrets + Scan your image for vulnerabilities + Clean npm cache + A generic list of ideas + Last: Lint your dockefile \ No newline at end of file From 53ca7816ea4507a7a6de12fc55ef9d9721c13223 Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 16 Aug 2020 17:21:49 +0300 Subject: [PATCH 0355/1795] Sorting the bullets --- README.md | 8 ++++---- sections/docker/memory-limit.md | 36 ++++++++++----------------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 434f77de0..1752631bb 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines 5. [Going To Production Practices (19) ](#5-going-to-production-practices) 6. [Security Practices (25)](#6-security-best-practices) 7. [Performance Practices (2) (Work In Progress️ ✍️)](#7-draft-performance-best-practices) -8. [Docker Practices (Work In Progress️ ✍️)](#7-draft-docker-best-practices) +8. [Docker Practices](#8-docker-best-practices)

@@ -1075,7 +1075,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E

⬆ Return to top

-# `7. Draft: Docker Best Practices` +# `8. Docker Best Practices` 🏅 Many thanks to [Bret Fisher](https://github.com/BretFisher) from whom we learned many of the following practices @@ -1145,9 +1145,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.7. Set memory limits using both Docker and v8 -**TL;DR:** Always configure a memory limit using Docker, optionally set also the v8 limits. Practically, use the Docker flag 'run --memory' or set the right values within the platform that runs Docker. By doing this, the runtime will be capable of making better decisions on when to scale, prevent one citizen from starving others, drive thoughtful crash decisions (e.g., Docker can allow slight burst deviations) and in-overall it's always better to move HW decisions to the OPS court +**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags: Set the v8's old space memory to be a bit less than the container limit -**Otherwise:** When setting limits using V8 --max-old-space-size the Docker runtime won't be aware of its capacity limits and will have to blindly place it in an instance that might not have the right size +**Otherwise:** The docker definition is needed to perform thoughful scaling decision and prevent starving of other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% if its host resources 🔗 [**Read More: Set memory limits using Docker only**](/sections/docker/memory-limit.md) diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md index 264dc89b2..1a27238ac 100644 --- a/sections/docker/memory-limit.md +++ b/sections/docker/memory-limit.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime. There is confusion around which of those to set: Ensure to always configure the Docker runtime limits as is has a much wider perspective for making the right decisions. First, given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Docker also measures the actually used memory and not the allocated part (unlike v8 --max-old-space-size) so it can get Killed only when really needed. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as is has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This is by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-10% of Docker's memory limit.

@@ -21,7 +21,7 @@ docker run --memory 512m my-node-app

-### Code Example – Memory limit with Kubernetes +### Code Example – Memory limit with Kubernetes and v8
Kubernetes deployment yaml @@ -40,29 +40,7 @@ spec: memory: "400Mi" limits: memory: "500Mi" - command: ["node index.js"] -``` - -
- -

- -### Code Example Anti-Pattern – Setting memory limit using V8 flags - -
- -Kubernetes deployment yaml - -``` -apiVersion: v1 -kind: Pod -metadata: - name: my-node-app -spec: - containers: - - name: my-node-app - image: my-node-app - command: ["node index.js --max-old-space-size=400"] + command: ["node index.js --max-old-space-size=350"] ```
@@ -82,3 +60,11 @@ From [K8S documentation](https://kubernetes.io/docs/tasks/configure-pod-containe From [Docker official docs](https://docs.docker.com/config/containers/resource_constraints/) > It is important not to allow a running container to consume too much of the host machine’s memory. On Linux hosts, if the kernel detects that there is not enough memory to perform important system functions, it throws an OOME, or Out Of Memory Exception, and starts killing processes to free up memory. + +

+ +### Node.js documentation: "V8 will spend more time on garbage collection" + +From [Node.js official docs](https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_megabytes) + +> Sets the max memory size of V8's old memory section. As memory consumption approaches the limit, V8 will spend more time on garbage collection in an effort to free unused memory. On a machine with 2GB of memory, consider setting this to 1536 (1.5GB) to leave some memory for other uses and avoid swapping. From 600c71ca3eea2514b54b16186b54f82fcf5653cb Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 16 Aug 2020 17:25:53 +0300 Subject: [PATCH 0356/1795] Sorting the bullets --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1752631bb..31af84c47 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@
- 86 items Last update: March, 2020 Updated for Node 13.1.0 + 101 items Last update: March, 2020 Updated for Node 13.1.0

@@ -54,7 +54,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines 5. [Going To Production Practices (19) ](#5-going-to-production-practices) 6. [Security Practices (25)](#6-security-best-practices) 7. [Performance Practices (2) (Work In Progress️ ✍️)](#7-draft-performance-best-practices) -8. [Docker Practices](#8-docker-best-practices) +8. [Docker Practices (15)](#8-docker-best-practices)

From b7cf446d60c00eb4fc8e505de54a4bcbc06e443a Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 16 Aug 2020 18:10:27 +0300 Subject: [PATCH 0357/1795] Sorting the bullets --- README.md | 2 +- sections/docker/graceful-shutdown.md | 33 +--------------------------- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 31af84c47..6a66b592b 100644 --- a/README.md +++ b/README.md @@ -1083,7 +1083,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to reduce overhead in size and vulnerabilities. +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. diff --git a/sections/docker/graceful-shutdown.md b/sections/docker/graceful-shutdown.md index bf73a8d68..7f13911f9 100644 --- a/sections/docker/graceful-shutdown.md +++ b/sections/docker/graceful-shutdown.md @@ -8,35 +8,8 @@ In a Dockerized runtime like Kubernetes, containers are born and die frequently.

-### Code Example – A graceful shutdown when using Express and Kubernetes -
- -JavaScript - -``` -TBD -``` - -
- -

- -### Code Example – Harsh shutdown - -
- -JavaScript - -``` -TBD -``` - -
- -

- -### Code Example – Making Node.js the root process allowing passing signals to the code +### Code Example – Placing Node.js as the root process allows passing signals to the code
@@ -109,7 +82,3 @@ CMD ["npm", "start"] From the blog, [Rising Stack](https://blog.risingstack.com/graceful-shutdown-node-js-kubernetes/) ![alt text](/assets/images/Kubernetes-graceful-shutdown-flowchart.png "The shutdown phases") - -``` - -``` From 6ad3357754e8ae582c26a5ffa029e443671fd3cd Mon Sep 17 00:00:00 2001 From: Yoni Date: Sun, 16 Aug 2020 18:32:47 +0300 Subject: [PATCH 0358/1795] Sorting the bullets --- README.md | 4 ++-- sections/docker/lint-dockerfile.md | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6a66b592b..070051a5f 100644 --- a/README.md +++ b/README.md @@ -1228,9 +1228,9 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.15. Lint your Dockerfile -**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. -**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user +**Otherwise:** Mistakenely the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. 🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md) diff --git a/sections/docker/lint-dockerfile.md b/sections/docker/lint-dockerfile.md index 8f70cb981..b9793fd4f 100644 --- a/sections/docker/lint-dockerfile.md +++ b/sections/docker/lint-dockerfile.md @@ -2,14 +2,14 @@ ### One Paragraph Explainer -As our core application code is linted to conform to best practices and eliminate issues and bugs before it could become a problem, so too should our Dockerfiles. Linting the Dockerfile means you can ensure that there aren’t any structural problems with the logic and instructions specified in your Dockerfiles. +As our core application code is linted to conform to best practices and eliminate issues and bugs before it could become a problem, so too should our Dockerfiles. Linting the Dockerfile means increasing the chances of catching production issues on time with very light effort. For example, it can ensure that there aren’t any structural problems with the logic and instructions specified in your Dockerfiles like trying to copy from non-existing stage, copying from unknown online repository, running the app with power user (SUDO) and many more. The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialized Dockerfile linter that aims to embrace the [Docker best practices.](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +
-### Code example: -The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialised Dockerfile linter that aims to embrace the [Docker best practices.](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +### Code example: Inspecting a Dockerfile using hadolint -```bash +```bash hadolint production.Dockerfile hadolint --ignore DL3003 --ignore DL3006 # exclude specific rules hadolint --trusted-registry my-company.com:500 # Warn when using untrusted FROM images From eccf0bac7480270d09facb4bd1a5566620a72300 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Mon, 17 Aug 2020 08:41:46 +0200 Subject: [PATCH 0359/1795] Add tldr for caching --- README.md | 6 +++--- ...er-build-time.md => use-cache-for-shorter-build-time.md} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename sections/docker/{Caching-for-shorter-build-time.md => use-cache-for-shorter-build-time.md} (89%) diff --git a/README.md b/README.md index 070051a5f..12ba80eb8 100644 --- a/README.md +++ b/README.md @@ -1155,11 +1155,11 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.8. Caching -**TL;DR:** +**TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. -**Otherwise:** +**Otherwise:** Docker build will be very long, and consume lot of resources even when tiny changes are made -🔗 [**Read More: Scan your image for vulnerabilities**](/sections/docker/file.md) +🔗 [**Read More: Leverage caching to reduce build times**](/sections/docker/use-cache-for-shorter-build-time.md)


diff --git a/sections/docker/Caching-for-shorter-build-time.md b/sections/docker/use-cache-for-shorter-build-time.md similarity index 89% rename from sections/docker/Caching-for-shorter-build-time.md rename to sections/docker/use-cache-for-shorter-build-time.md index 8376966f9..c3bdaaba4 100644 --- a/sections/docker/Caching-for-shorter-build-time.md +++ b/sections/docker/use-cache-for-shorter-build-time.md @@ -2,9 +2,9 @@ ## One paragraph explainer -Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identicals or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer, all the subsquent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes everytime you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. +Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identical or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer, all the subsequent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes every time you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. -![Docker layers](/assests/images/docker_layers_schema.png) +![Docker layers](/assets/images/docker_layers_schema.png) * Image taken from [Digging into Docker layers](https://medium.com/@jessgreb01/digging-into-docker-layers-c22f948ed612) by jessgreb01* From 1abb12a536ac38547d6880a91e21bd52082df0cf Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Mon, 17 Aug 2020 09:56:20 +0200 Subject: [PATCH 0360/1795] basic fixes: typos and punctuation --- README.md | 28 +++++++++---------- sections/docker/avoid-build-time-secrets.md | 4 +-- sections/docker/clean-cache.md | 4 +-- sections/docker/docker-ignore.md | 2 +- sections/docker/graceful-shutdown.md | 4 +-- sections/docker/image-tags.md | 6 ++-- sections/docker/install-for-production.md | 2 +- sections/docker/memory-limit.md | 2 +- sections/docker/multi_stage_builds.md | 8 ++---- .../docker/restart-and-replicate-processes.md | 4 +-- sections/docker/scan-images.md | 2 +- sections/docker/smaller_base_images.md | 11 ++++---- .../use-cache-for-shorter-build-time.md | 2 +- 13 files changed, 36 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 12ba80eb8..c18016931 100644 --- a/README.md +++ b/README.md @@ -1103,7 +1103,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.3. Let the Docker runtime handle replication and uptime -**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediating process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes +**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes **Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance @@ -1124,7 +1124,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.5. Clean-up dependencies before production -**TL;DR:** Althoug DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so gurantee that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' +**TL;DR:** Although DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' **Otherwise:** Many of the infamous npm security breaches were found within development packages @@ -1135,7 +1135,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.6. Shutdown smartly and gracefully -**TL;DR:** Handle the process SIGTERM event and clean-up all existing conenction and resources. This should be done while responding to ongoing reqeusts. In Dockerized runtimes, shuting down containers is not a rare event rather a frequent occurence that happen as part of routine work. Acheiving this demand some thoughful code to orchestrate few moving parts: The load balancer, keep-alive connections, the HTTP server and other resources +**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources **Otherwise:** Dying immediately means not responding to thousands of disappointed users @@ -1147,7 +1147,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags: Set the v8's old space memory to be a bit less than the container limit -**Otherwise:** The docker definition is needed to perform thoughful scaling decision and prevent starving of other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% if its host resources +**Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources 🔗 [**Read More: Set memory limits using Docker only**](/sections/docker/memory-limit.md) @@ -1157,22 +1157,22 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. -**Otherwise:** Docker build will be very long, and consume lot of resources even when tiny changes are made +**Otherwise:** Docker build will be very long, and consume lot of resources even when making tiny changes 🔗 [**Read More: Leverage caching to reduce build times**](/sections/docker/use-cache-for-shorter-build-time.md)


-## ![✔] 8.9. Use explicit image reference, avoid 'latest' tag +## ![✔] 8.9. Use explicit image reference, avoid `latest` tag -**TL;DR:** The latest tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. +**TL;DR:** The `latest` tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. -In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a determinstic install is expected, a SHA256 digest can be used to reference an exact image. +In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. **Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. -🔗 [**Read More: Understand image tags, and use the "latest" tag with caution**](/sections/docker/image-tags.md) +🔗 [**Read More: Understand image tags and use the "latest" tag with caution**](/sections/docker/image-tags.md)


@@ -1180,7 +1180,7 @@ In addition, referring to an image tag means that the base image is subject to c **TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Alpine Linux variants, mitigates this issue. -**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors, and more resources are consumed. +**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. 🔗 [**Read More: Prefer smaller images**](/sections/docker/smaller_base_images.md) @@ -1189,10 +1189,9 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args -**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment, like CI and a registry, that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like .npmrc and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces +**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces - -**Otherwise:** Everyone with access to the CI and docker registry will also get as a bonus access to some precious organization secrets +**Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus 🔗 [**Read More: Clean-out build-time secrets**](/sections/docker/avoid-build-time-secrets.md) @@ -1209,8 +1208,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.13 Clean NODE_MODULE cache -**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off - +**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off **Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used diff --git a/sections/docker/avoid-build-time-secrets.md b/sections/docker/avoid-build-time-secrets.md index 460b18384..2d33ee088 100644 --- a/sections/docker/avoid-build-time-secrets.md +++ b/sections/docker/avoid-build-time-secrets.md @@ -5,7 +5,7 @@ ### One Paragraph Explainer -A Docker image isn't just a bunch of files but rather multiple layers revealing what happened during build-time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however in fact this token can now be fetched from the developers machine Docker history, from the Docker registry and the CI. An attacker who get access to that token, is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not ship the secrets with the images but will appear in the local Docker history - This is typically considered as secured enough for most organizations. +A Docker image isn't just a bunch of files but rather multiple layers revealing what happened during build-time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however this token can now be fetched from the developer's machine Docker history, from the Docker registry and the CI. An attacker who gets access to that token is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not ship the secrets with the images but will appear in the local Docker history - This is typically considered as secured enough for most organizations.

@@ -50,7 +50,7 @@ FROM build as prod COPY --from=build /dist /dist CMD ["node","index.js"] -# The ARG and .npmrc won't appear in the final image, but can be found in the Docker daemon un-tagged images list - make sure to delete those +# The ARG and .npmrc won't appear in the final image but can be found in the Docker daemon un-tagged images list - make sure to delete those ```
diff --git a/sections/docker/clean-cache.md b/sections/docker/clean-cache.md index 625ca40a2..3f08f0d46 100644 --- a/sections/docker/clean-cache.md +++ b/sections/docker/clean-cache.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container, this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag +Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag

@@ -22,4 +22,4 @@ RUN npm ci --production && npm clean cache --force # The rest comes here ``` - \ No newline at end of file + diff --git a/sections/docker/docker-ignore.md b/sections/docker/docker-ignore.md index 4b0f31f94..f7e754636 100644 --- a/sections/docker/docker-ignore.md +++ b/sections/docker/docker-ignore.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -The Docker build command copies the local files into the build context environment over a virtual network. Be careful - development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world, the Dockerfile should be explicit about what is being copied. On top of this, include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the build can better utilize the cache and achieve better performance +The Docker build command copies the local files into the build context environment over a virtual network. Be careful - development and CI folders contain secrets like .npmrc, .aws, .env files, and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world the Dockerfile should be explicit about what is being copied. On top of this include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the builder can better utilize the cache and achieve better performance

diff --git a/sections/docker/graceful-shutdown.md b/sections/docker/graceful-shutdown.md index 7f13911f9..52c889860 100644 --- a/sections/docker/graceful-shutdown.md +++ b/sections/docker/graceful-shutdown.md @@ -4,12 +4,12 @@ ### One Paragraph Explainer -In a Dockerized runtime like Kubernetes, containers are born and die frequently. This happens not only when errors are thrown but also for good reasons like relocating containers, replacing them with a newer version and more. It's achieved by sending a notice (SIGTERM signal) to the process with a 30 second grace period. This puts a challenge on the developer to ensure the app is handling the ongoing requests and clean-up resources in a timely fashion. Otherwise, thousands of sad users will not get a response. Implementation-wise, the shutdown code should wait until all ongoing requests are flushed out and then clean-up resources. Easier said than done, practically it demands orchestrating few parts: Tell the LoadBalancer that the app is not ready to server more requests (via health-check), wait that existing requests are done, avoid handling new requests, clean-up resources and finally log some useful information before dying. If Keep-Alive connections are being used, the clients also must be notified that a new connection should be established - A library like [Stoppable](https://github.com/hunterloftis/stoppable) can greatly help achieving this. +In a Dockerized runtime like Kubernetes, containers are born and die frequently. This happens not only when errors are thrown but also for good reasons like relocating containers, replacing them with a newer version and more. It's achieved by sending a notice (SIGTERM signal) to the process with a 30 second grace period. This puts a challenge on the developer to ensure the app is handling the ongoing requests and clean-up resources in a timely fashion. Otherwise thousands of sad users will not get a response. Implementation-wise, the shutdown code should wait until all ongoing requests are flushed out and then clean-up resources. Easier said than done, practically it demands orchestrating several parts: Tell the LoadBalancer that the app is not ready to serve more requests (via health-check), wait for existing requests to be done, avoid handling new requests, clean-up resources and finally log some useful information before dying. If Keep-Alive connections are being used, the clients must also be notified that a new connection should be established - A library like [Stoppable](https://github.com/hunterloftis/stoppable) can greatly help achieving this.

-### Code Example – Placing Node.js as the root process allows passing signals to the code +### Code Example – Placing Node.js as the root process allows passing signals to the code (see [/sections/docker/bootstrap-using-node.md])
diff --git a/sections/docker/image-tags.md b/sections/docker/image-tags.md index 4584b12ce..293dabdd4 100644 --- a/sections/docker/image-tags.md +++ b/sections/docker/image-tags.md @@ -1,10 +1,8 @@ -# Understand image tags vs digests, and use the "latest" tag with caution +# Understand image tags vs digests and use the `:latest` tag with caution ### One Paragraph Explainer -If this is a production situation, and security and stability are important, then just "convenience" is likely not the best deciding factor. - -In addition, the 'latest' tag is Docker's default tag. This means that a developer who forgets to add an explicit tag, will accidentally push a new version of an image as 'latest', which might end in very unintended results if the latest tag is being relied upon as the latest production image. +If this is a production situation and security and stability are important then just "convenience" is likely not the best deciding factor. In addition the `:latest` tag is Docker's default tag. This means that a developer who forgets to add an explicit tag will accidentally push a new version of an image as `latest`, which might end in very unintended results if the `latest` tag is being relied upon as the latest production image. ### Code example: diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md index a597e5e4d..eecbf4080 100644 --- a/sections/docker/install-for-production.md +++ b/sections/docker/install-for-production.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependecies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons, the image that is finally shipped to production should be safe and minimal. Running npm install with a --production is a great start however it get even safer to run 'npm ci' that ensure a fresh install and the existence of lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, multi stage builds can help in having different sets of dependencies and finally only those for production (See dedicated bullet on multi stage builds) +Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependecies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons the image that is finally shipped to production should be safe and minimal. Running npm install with a `--production` is a great start, however it gets even safer to run 'npm ci' that ensures a fresh install and the existence of a lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, multi stage builds can help in having different sets of dependencies and finally only those for production (See dedicated bullet on multi stage builds)

diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md index 1a27238ac..16af2df40 100644 --- a/sections/docker/memory-limit.md +++ b/sections/docker/memory-limit.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink alone all the juice and leave other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as is has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, With Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This is by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-10% of Docker's memory limit. +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink all the juice alone and leaves other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as it has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, with Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-10% of Docker's memory limit.

diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md index ae50f5d97..42217e042 100644 --- a/sections/docker/multi_stage_builds.md +++ b/sections/docker/multi_stage_builds.md @@ -2,7 +2,7 @@ ### One Paragraph Explainer -Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes, you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime, such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). +Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime (see [/sections/docker/avoid-build-time-secrets.md]), such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). ### Example @@ -29,9 +29,7 @@ node_modules docs ``` -Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image, -and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage, -and then install production dependencies. +Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage and then install production dependencies. ```dockerfile # Start with fully-featured Node.js base image @@ -68,4 +66,4 @@ RUN yarn install --frozen-lockfile --production COPY --chown=node:node --from=build /home/node/app/dist ./dist CMD [ "node", "dist/app.js" ] -``` \ No newline at end of file +``` diff --git a/sections/docker/restart-and-replicate-processes.md b/sections/docker/restart-and-replicate-processes.md index 0f03c1d90..eaf8be505 100644 --- a/sections/docker/restart-and-replicate-processes.md +++ b/sections/docker/restart-and-replicate-processes.md @@ -4,11 +4,11 @@ ### One Paragraph Explainer -Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and take into account many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that, some may tempt to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side, When using local tools for restarting the process, the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone. +Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and take into account many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that some may be tempted to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side when using local tools for restarting the process the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone.

-### Code Example – Invoking Node.js directly without intermediating tools +### Code Example – Invoking Node.js directly without intermediate tools
diff --git a/sections/docker/scan-images.md b/sections/docker/scan-images.md index 8f3411d16..3e09f81a4 100644 --- a/sections/docker/scan-images.md +++ b/sections/docker/scan-images.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Scanning the code for vulnerabilities is a valuable act, but it doesn't cover all the potential threats. Why? Because vulnerabilities also exist on the OS level and the app might execute those binaries like Shell, Tarball, OpenSSL. Also, vulnerable dependencies might be injected after the code scan (i.e. supply chain attacks) - hence scanning the final image just before production is in order. This idea resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the assmebled deliverable. There are 3 main scanner families: Local/CI binaries with a cached vulnerabilities DB, scanners as a service in the cloud and a niche of tools which scan during the docker build itself. The first group is the most popular and usually the fastest - Tools like [Trivvy](https://github.com/aquasecurity/trivy), [Anchore](https://github.com/anchore/anchore) and [Snyk](https://support.snyk.io/hc/en-us/articles/360003946897-Container-security-overview) are worth exploring. Most CI vendors provide a local plugin that facilitates the interaction with these scanners. It should be noted that these scanners cover a lot of ground and therefore will show findings in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed +Scanning the code for vulnerabilities is a valuable act but it doesn't cover all the potential threats. Why? Because vulnerabilities also exist on the OS level and the app might execute those binaries like Shell, Tarball, OpenSSL. Also, vulnerable dependencies might be injected after the code scan (i.e. supply chain attacks) - hence scanning the final image just before production is in order. This idea resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the assembled deliverable. There are 3 main scanner families: Local/CI binaries with a cached vulnerabilities DB, scanners as a service in the cloud and a niche of tools which scan during the docker build itself. The first group is the most popular and usually the fastest - Tools like [Trivvy](https://github.com/aquasecurity/trivy), [Anchore](https://github.com/anchore/anchore) and [Snyk](https://support.snyk.io/hc/en-us/articles/360003946897-Container-security-overview) are worth exploring. Most CI vendors provide a local plugin that facilitates the interaction with these scanners. It should be noted that these scanners cover a lot of ground and therefore will show findings in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed

diff --git a/sections/docker/smaller_base_images.md b/sections/docker/smaller_base_images.md index d1fb358dc..d394954d3 100644 --- a/sections/docker/smaller_base_images.md +++ b/sections/docker/smaller_base_images.md @@ -1,14 +1,13 @@ # Prefer smaller Docker base images -Large Docker images can lead to higher exposure to vulnerabilities and increased resource consumption. Often, you don't need certain packages installed at runtime that are needed for building. -Pulling and storing larger images will become more expensive at scale, when dealing with larger images. Minimal images may not come with common libraries needed for building native modules or packages -useful for debugging (e.g. curl) pre-installed, by design. Using the Alpine Linux variants of images, can lead to a reduced footprint in terms of resources used and the amount of attack vectors -present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version, which is almost 10x smaller. A Slim variant based on Debian, which is only 38MB in size and contains the minimal -packages needed to run Node.js, is also a great choice. +Large Docker images can lead to higher exposure to vulnerabilities and increased resource consumption. Often you don't need certain packages installed at runtime that are needed for building. +Pulling and storing larger images will become more expensive at scale, when dealing with larger images. By design minimal images may not come with common libraries needed for building native modules or packages useful for debugging (e.g. curl) pre-installed. +Using the Alpine Linux variants of images can lead to a reduced footprint in terms of resources used and the amount of attack vectors present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version, which is almost 10x smaller. +A Slim variant based on Debian, which is only 38MB in size and contains the minimal packages needed to run Node.js, is also a great choice. ### Blog Quote: "If you want to shrink your Docker images, have your services start faster and be more secure then try Alpine out." From [Nick Janetakis' blog](https://nickjanetakis.com/blog/the-3-biggest-wins-when-using-alpine-as-a-base-docker-image) > It’s no secret by now that Docker is heavily using Alpine as a base image for official Docker images. This movement started near the beginning of 2016. [...] - When pulling down new Docker images onto a fresh server, you can expect the initial pull to be quite a bit faster on Alpine. The slower your network is, the bigger the difference it will be. [...] Another perk of being much smaller in size is that the surface area to be attacked is much less. When there’s not a lot of packages and libraries on your system, there’s very little that can go wrong. \ No newline at end of file + When pulling down new Docker images onto a fresh server, you can expect the initial pull to be quite a bit faster on Alpine. The slower your network is, the bigger the difference it will be. [...] Another perk of being much smaller in size is that the surface area to be attacked is much less. When there’s not a lot of packages and libraries on your system, there’s very little that can go wrong. diff --git a/sections/docker/use-cache-for-shorter-build-time.md b/sections/docker/use-cache-for-shorter-build-time.md index c3bdaaba4..3e7ffaae3 100644 --- a/sections/docker/use-cache-for-shorter-build-time.md +++ b/sections/docker/use-cache-for-shorter-build-time.md @@ -2,7 +2,7 @@ ## One paragraph explainer -Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identical or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer, all the subsequent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build, the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes every time you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. +Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identical or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer all the subsequent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build; the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes every time you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. ![Docker layers](/assets/images/docker_layers_schema.png) From f2974df256820f867af7a58d76771e1627507fb7 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Mon, 17 Aug 2020 10:12:13 +0200 Subject: [PATCH 0361/1795] Add Kevyn as steering committee member --- README.md | 15 ++++++++++++--- assets/images/members/kevyn.png | Bin 993803 -> 102955 bytes 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5612eb5fa..5c0c4289f 100644 --- a/README.md +++ b/README.md @@ -1303,6 +1303,15 @@ Full Stack Developer & Site Reliability Engineer based in New Zealand, intereste
+ + +[Kevyn Bruyere](https://github.com/kevynb) + + +Independent full-stack developer with a taste for Ops and automation. + +
+ ### Steering Committee Emeriti @@ -1322,9 +1331,9 @@ Thank you to all our collaborators! 🙏 Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 -| | | | -| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | -| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | [Kevyn Bruyere](https://github.com/kevynb) | +| | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | ### Collaborator Emeriti diff --git a/assets/images/members/kevyn.png b/assets/images/members/kevyn.png index bd97c1d215947be2b10ca8774eb8072c431c17d8..a99f56e1514e8f4d1bc285eab61bf11e881b94e2 100644 GIT binary patch literal 102955 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 qVZ|Hs$^Zc1p@FuB)&G;oSR(M3qXtGu0{LT! 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

  • 65My4s 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

    =N$>fX*p!JD}i9M0v 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 From 2dddd7a7e9c782a62cada689f034e8ef603aa03a Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Mon, 17 Aug 2020 10:21:31 +0200 Subject: [PATCH 0362/1795] Add note about node-cache and multi-stage builds --- sections/docker/clean-cache.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sections/docker/clean-cache.md b/sections/docker/clean-cache.md index 3f08f0d46..70669f747 100644 --- a/sections/docker/clean-cache.md +++ b/sections/docker/clean-cache.md @@ -4,7 +4,9 @@ ### One Paragraph Explainer -Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag +Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag. + +*Please not that this is not relevant if you are using a multi-stage build as long as you don't install new packages in the last stage*

    From a6de46da68c55b367f8afdf40302f74d4b893206 Mon Sep 17 00:00:00 2001 From: Siddharth Goel Date: Mon, 17 Aug 2020 19:40:10 +0530 Subject: [PATCH 0363/1795] Typo Fixes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5612eb5fa..c29497da1 100644 --- a/README.md +++ b/README.md @@ -1103,7 +1103,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.3. Remove development dependencies -**TL;DR:** Althoug DevDepencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development depdencies. Doing so gurantess that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' +**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped, and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' **Otherwise:** Many of the infamous npm security breaches were found within development packages @@ -1203,9 +1203,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] Use .dockerignore to prevent leaking secrets -**TL;DR:** Include a .dockerignore file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus, the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker +**TL;DR:** Include a `.dockerignore` file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus, the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker -**Otherwise:** Common personal secret files like .env, .aws and .npmrc will be shared with anybody with access to the image (e.g. Docker repository) +**Otherwise:** Common personal secret files like `.env`, `.aws` and `.npmrc` will be shared with anybody with access to the image (e.g. Docker repository) 🔗 [**Read More: On the importance of docker ignore**](/sections/docker/docker-ignore.md) From 9683df5e3ab95aa28a00e7e3a55e665f5fcf1ee6 Mon Sep 17 00:00:00 2001 From: Ryan Smith <1578766+ryan3E0@users.noreply.github.com> Date: Tue, 18 Aug 2020 22:05:29 -0400 Subject: [PATCH 0364/1795] Add convict link to additional config spots --- README.brazilian-portuguese.md | 2 +- README.chinese.md | 2 +- README.korean.md | 2 +- README.md | 2 +- README.polish.md | 2 +- README.russian.md | 2 +- sections/projectstructre/configguide.brazilian-portuguese.md | 2 +- sections/projectstructre/configguide.chinese.md | 2 +- sections/projectstructre/configguide.korean.md | 2 +- sections/projectstructre/configguide.md | 2 +- sections/projectstructre/configguide.polish.md | 2 +- sections/projectstructre/configguide.russian.md | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.brazilian-portuguese.md b/README.brazilian-portuguese.md index e98bb87b8..7b8675192 100644 --- a/README.brazilian-portuguese.md +++ b/README.brazilian-portuguese.md @@ -100,7 +100,7 @@ Leia em diferentes idiomas: [![CN](/assets/flags/CN.png)**CN**](/README.chinese. ## ![✔] 1.5 Use configuração consciente, segura e hierárquica do ambiente -**TL;DR:** Uma definição de configuração perfeita e impecável deve garantir que (a) as chaves possam ser lidas a partir do arquivo E TAMBÉM da variável de ambiente (b) os segredos sejam mantidos fora do código consolidado (c) a configuração é hierárquica para facilitar a localização. Existem alguns pacotes que podem auxiliar na checagem destes tópicos, como [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) e [config](https://www.npmjs.com/package/config) +**TL;DR:** Uma definição de configuração perfeita e impecável deve garantir que (a) as chaves possam ser lidas a partir do arquivo E TAMBÉM da variável de ambiente (b) os segredos sejam mantidos fora do código consolidado (c) a configuração é hierárquica para facilitar a localização. Existem alguns pacotes que podem auxiliar na checagem destes tópicos, como [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) e [convict](https://www.npmjs.com/package/convict) **Caso contrário:** Deixar de satisfazer qualquer um dos requisitos de configuração simplesmente atrapalhará a equipe de desenvolvimento ou devops. Provavelmente ambas. diff --git a/README.chinese.md b/README.chinese.md index 647b60015..01c7ff41b 100644 --- a/README.chinese.md +++ b/README.chinese.md @@ -82,7 +82,7 @@ ## ![✔] 1.5 使用易于设置环境变量,安全和分级的配置 -**TL;DR:** 一个完美无瑕的配置安装应该确保 (a) 元素可以从文件中,也可以从环境变量中读取 (b) 密码排除在提交的代码之外 (c) 为了易于检索,配置是分级的。仅有几个包可以满足这样的条件,比如[rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) 和 [config](https://www.npmjs.com/package/config)。 +**TL;DR:** 一个完美无瑕的配置安装应该确保 (a) 元素可以从文件中,也可以从环境变量中读取 (b) 密码排除在提交的代码之外 (c) 为了易于检索,配置是分级的。仅有几个包可以满足这样的条件,比如[rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) 和 [convict](https://www.npmjs.com/package/convict)。 **否则:** 不能满足任意的配置要求将会使开发,运维团队,或者两者,易于陷入泥潭。 diff --git a/README.korean.md b/README.korean.md index b4590d18b..726206ad7 100644 --- a/README.korean.md +++ b/README.korean.md @@ -104,7 +104,7 @@ ## ![✔] 1.5 환경을 인식하는, 보안적인, 계층적인 설정을 사용하라 -**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경변수 모두에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 밖에서 관리되어야하며 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. +**핵심요약:** 완벽하고 결점이 없는 구성 설정은 (a) 파일과 환경변수 모두에서 키 값을 읽을 수 있어야하고 (b) 보안 값들은 커밋된 코드 밖에서 관리되어야하며 (c) 설정은 좀 더 쉽게 찾을 수 있도록 계층적으로 관리해야 한다. [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), [convict](https://www.npmjs.com/package/convict)와 같이 이러한 요구사항을 동작하게 해주는 몇가지 패키지가 존재한다. **그렇게 하지 않을 경우:** 위의 구성 요구사항 중 하나라도 만족시키지 못한다면 개발팀이나 데브옵스팀을 늪으로 몰아갈 수 있다. 십중팔구 둘 다. diff --git a/README.md b/README.md index 5c0c4289f..3514d05f1 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines ## ![✔] 1.5 Use environment aware, secure and hierarchical config -**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) +**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict). **Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both diff --git a/README.polish.md b/README.polish.md index 511d56fa0..9d3accbd7 100644 --- a/README.polish.md +++ b/README.polish.md @@ -99,7 +99,7 @@ Przeczytaj także w innych językach: [![CN](/assets/flags/CN.png)**CN**](/READM ## ![✔] 1.5 Używaj konfiguracji przyjaznej środowisku, bezpiecznej i hierarchicznej - **TL;DR:** Idealne i bezbłędne ustawienie konfiguracji powinno zapewnić, że (a) klucze można odczytać z pliku ORAZ ze zmiennych środowiskowych (b) dane wrażliwe są przechowywane poza zatwierdzonym kodem (c) konfiguracja jest hierarchiczna dla łatwiejszego wyszukiwania. Istnieje kilka pakietów, które mogą pomóc zaznaczyć większość z tych pól, takich jak [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf ) i [config](https://www.npmjs.com/package/config) + **TL;DR:** Idealne i bezbłędne ustawienie konfiguracji powinno zapewnić, że (a) klucze można odczytać z pliku ORAZ ze zmiennych środowiskowych (b) dane wrażliwe są przechowywane poza zatwierdzonym kodem (c) konfiguracja jest hierarchiczna dla łatwiejszego wyszukiwania. Istnieje kilka pakietów, które mogą pomóc zaznaczyć większość z tych pól, takich jak [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) i [convict](https://www.npmjs.com/package/convict) **W przeciwnym razie:** Niespełnienie któregokolwiek z wymagań konfiguracji po prostu ugrzęźnie w zespole programistów lub DevOps. Prawdopodobnie jedno i drugie diff --git a/README.russian.md b/README.russian.md index e332b40ab..272e6d12b 100644 --- a/README.russian.md +++ b/README.russian.md @@ -100,7 +100,7 @@ ## ![✔] 1.5 Используйте безопасную и иерархическую конфигурацию с учетом среды -**TL;DR:** Идеальная и безупречная конфигурация должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) +**TL;DR:** Идеальная и безупречная конфигурация должна обеспечивать (а) считывание ключей из файла И из переменной среды, (б) хранение секретов вне основной кодовой базы, (в) иерархическую структуру для облегчения поиска. Есть несколько пакетов, которые могут помочь поставить галочку в большинстве таких полей, как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict) **Иначе:** Невыполнение каких-либо требований к конфигурации приведет к срывам в работе разработчиков или devops командой. Вероятно, и тех и других. diff --git a/sections/projectstructre/configguide.brazilian-portuguese.md b/sections/projectstructre/configguide.brazilian-portuguese.md index 92a74f51d..39eda1fe7 100644 --- a/sections/projectstructre/configguide.brazilian-portuguese.md +++ b/sections/projectstructre/configguide.brazilian-portuguese.md @@ -14,7 +14,7 @@ Ao lidar com dados de configuração, muitas coisas podem simplesmente incomodar 4. Alguns cenários de configuração avançada exigem a injeção de valores de configuração via linha de comando (vargs) ou informações de configuração de sincronização por meio de um cache centralizado, como o Redis, para que vários servidores usem os mesmos dados de configuração. -Algumas bibliotecas de configuração podem fornecer a maioria desses recursos gratuitamente, dê uma olhada nas bibliotecas npm como [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) e [config](https://www.npmjs.com/package/config) que satisfazem muitos desses requisitos. +Algumas bibliotecas de configuração podem fornecer a maioria desses recursos gratuitamente, dê uma olhada nas bibliotecas npm como [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) e [convict](https://www.npmjs.com/package/convict) que satisfazem muitos desses requisitos.

    diff --git a/sections/projectstructre/configguide.chinese.md b/sections/projectstructre/configguide.chinese.md index 2efeb2ba1..741793e20 100644 --- a/sections/projectstructre/configguide.chinese.md +++ b/sections/projectstructre/configguide.chinese.md @@ -7,7 +7,7 @@ 当我们处理配置参数时,常常会很慢并且很烦躁:(1)当需要注入100个keys(而不是只在配置文件中提交它们)时,使用进程环境变量设置所有的keys变得非常繁琐,但是当处理只有devops管理权限的文件时,不改变代码行为就不不会变。一个可靠的配置解决方案必须结合配置文件和进程变量覆盖。(2)枚举一个普通JSON的所有keys时,当目录变得非常庞杂的时候,查找修改条目困难。几乎没有配置库允许将配置存储在多个文件中,运行时将所有文件联合起来。分成几个部分的分层JSON文件能够克服这个问题。请参照下面示例。(3)不推荐存储像密码数据这样的敏感信息,但是又没有快速便捷的方法解决这个难题。一些配置库允许文件加密,其他库在Git提交时加密目录,或者不存储这些目录的真实值,在通过环境变量部署期间枚举真实值。(4)一些高级配置场景需要通过命令行(vargs)注入配置值,或者像Redis一样通过集中缓存同步配置信息,所以不同的服务器不会保存不同的数据。 -一些配置库可以免费提供这些功能的大部分功能,请查看NPM库([nconf](https://www.npmjs.com/package/nconf) 和 [config](https://www.npmjs.com/package/config))这些库可以满足这些要求中的许多要求。 +一些配置库可以免费提供这些功能的大部分功能,请查看NPM库([nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) 和 [convict](https://www.npmjs.com/package/convict))这些库可以满足这些要求中的许多要求。

    diff --git a/sections/projectstructre/configguide.korean.md b/sections/projectstructre/configguide.korean.md index 6e7b36633..930941884 100644 --- a/sections/projectstructre/configguide.korean.md +++ b/sections/projectstructre/configguide.korean.md @@ -14,7 +14,7 @@ When dealing with configuration data, many things can just annoy and slow down: 4. some advanced configuration scenarios demand to inject configuration values via command line (vargs) or sync configuration info via a centralized cache like Redis so multiple servers will use the same configuration data. -Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) which tick many of these requirements. +Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict) which tick many of these requirements.

    diff --git a/sections/projectstructre/configguide.md b/sections/projectstructre/configguide.md index 24a419f1f..81d7918a0 100644 --- a/sections/projectstructre/configguide.md +++ b/sections/projectstructre/configguide.md @@ -16,7 +16,7 @@ When dealing with configuration data, many things can just annoy and slow down: 5. the application should fail as fast as possible and provide the immediate feedback if the required environment variables are not present at start-up, this can be achieved by using [convict](https://www.npmjs.com/package/convict) to validate the configuration. -Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) and [config](https://www.npmjs.com/package/config) which tick many of these requirements. +Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict) which tick many of these requirements.

    diff --git a/sections/projectstructre/configguide.polish.md b/sections/projectstructre/configguide.polish.md index d3d9bd7b4..fc871afaf 100644 --- a/sections/projectstructre/configguide.polish.md +++ b/sections/projectstructre/configguide.polish.md @@ -16,7 +16,7 @@ W przypadku danych konfiguracyjnych wiele rzeczy może po prostu denerwować i s 5. aplikacja powinna zakończyć się tak szybko, jak to możliwe i przekazać natychmiastowe informacje zwrotne, jeśli wymagane zmienne środowiskowe nie są obecne podczas uruchamiania, można to osiągnąć za pomocą [convict](https://www.npmjs.com/package/convict) aby sprawdzić poprawność konfiguracji. -Niektóre biblioteki konfiguracyjne mogą zapewniać większość tych funkcji za darmo, zobacz biblioteki npm, takie jak [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) i [config](https://www.npmjs.com/package/config), które spełniają wiele z tych wymagań. +Niektóre biblioteki konfiguracyjne mogą zapewniać większość tych funkcji za darmo, zobacz biblioteki npm, takie jak [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) i [convict](https://www.npmjs.com/package/convict) które spełniają wiele z tych wymagań.

    diff --git a/sections/projectstructre/configguide.russian.md b/sections/projectstructre/configguide.russian.md index 5a81fc963..bfeddd13e 100644 --- a/sections/projectstructre/configguide.russian.md +++ b/sections/projectstructre/configguide.russian.md @@ -16,7 +16,7 @@ 5. Приложение должно работать как можно быстрее и обеспечивать немедленную обратную связь, если требуемые переменные среды отсутствуют при запуске, можно использовать [convict](https://www.npmjs.com/package/convict) для проверки конфигурации. -Некоторые библиотеки конфигурации могут предоставить большинство этих функций бесплатно, посмотрите библиотеки npm, такие как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf) и [config](https://www.npmjs.com/package/config), которые отмечают многие из этих требований. +Некоторые библиотеки конфигурации могут предоставить большинство этих функций бесплатно, посмотрите библиотеки npm, такие как [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config) и [convict](https://www.npmjs.com/package/convict), которые отмечают многие из этих требований.

    From 85d315e91146c893a728dfbe2870a04a57df067c Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 19 Aug 2020 08:52:56 +0200 Subject: [PATCH 0365/1795] Proof-reading the docker section of README --- README.md | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7b7c5be49..fb6349c47 100644 --- a/README.md +++ b/README.md @@ -1083,9 +1083,9 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds, these resources can be used during build, while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats -**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities, and secrets only meant for the build phase might be leaked. +**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. 🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md) @@ -1113,13 +1113,12 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.4. Use .dockerignore to prevent leaking secrets -**TL;DR**: Include a .dockerignore file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus, the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker +**TL;DR**: Include a .dockerignore file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker **Otherwise**: Common personal secret files like .env, .aws and .npmrc will be shared with anybody with access to the image (e.g. Docker repository) 🔗 [**Read More: Use .dockerignore**](/sections/docker/lint-dockerfile.md) -


    ## ![✔] 8.5. Clean-up dependencies before production @@ -1130,7 +1129,6 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E 🔗 Read More: [Remove development dependencies](/sections/docker/install-for-production.md) -


    ## ![✔] 8.6. Shutdown smartly and gracefully @@ -1157,16 +1155,15 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. -**Otherwise:** Docker build will be very long, and consume lot of resources even when making tiny changes +**Otherwise:** Docker build will be very long and consume lot of resources even when making tiny changes 🔗 [**Read More: Leverage caching to reduce build times**](/sections/docker/use-cache-for-shorter-build-time.md) -


    ## ![✔] 8.9. Use explicit image reference, avoid `latest` tag -**TL;DR:** The `latest` tag can be misleading, and is subject to much confusion. Developers are often led to believe that specifying the latest tag will provide them with the most recent image in the repository, however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. +**TL;DR:** The `latest` tag can be misleading and is subject to much confusion. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. @@ -1184,7 +1181,6 @@ In addition, referring to an image tag means that the base image is subject to c 🔗 [**Read More: Prefer smaller images**](/sections/docker/smaller_base_images.md) -


    ## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args @@ -1199,16 +1195,15 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.12. Scan images for multi layers of vulnerabilities -**TL;DR:** Besides checking code dependencies vulnerabilities, also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins - -**Otherwise:** Your code might be entirely free from vulnerabilities. However, it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications +**TL;DR:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins +**Otherwise:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications 🔗 [**Read More: Generic Docker practices**](/sections/docker/scan-images.md) ## ![✔] 8.13 Clean NODE_MODULE cache -**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off +**TL;DR:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off **Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used @@ -1218,7 +1213,6 @@ In addition, referring to an image tag means that the base image is subject to c **TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. - 🔗 [**Read More: Generic Docker practices**](/sections/docker/generic-tips.md)


    From 45add8cda41ae14740f77da9767445ae33df1913 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 19 Aug 2020 09:10:22 +0200 Subject: [PATCH 0366/1795] Remove temp.md file --- temp.md | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 temp.md diff --git a/temp.md b/temp.md deleted file mode 100644 index d9cd06890..000000000 --- a/temp.md +++ /dev/null @@ -1,38 +0,0 @@ -TL;DR: Althoug DevDepencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development depdencies. Doing so gurantess that only neccessary code is shipped and the amount of potnetial attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' - -Otherwise: Many of the infamous npm security breaches were found within development packages - -🔗 Read More: Remove development dependencies - - -8.1. Clean NODE_MODULE cache - -**TL;DR:** After installing dependencies in a container, remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. By doing so, using a single line of code, tens of MB, typically 10-50% of the image size are shaved off - - -**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used - -🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) - -## ![✔] 8.4. Lint your Dockerfile - -**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. - -**Otherwise:** A Docker image built with errors or performance bottlenecks could result in security issues in production, or differing from best practices to the detriment of the application end user. - -🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md) - -Use multistage builds - Bootstrap the code using 'node' command, avoid 'npm run' scripts - Install packages for production - Dockerignore - Graceful shutdown - Set Docker memory limits - Utilize caching for better build time - Don't use "latest", use a digest or specific tag - Prefer smaller images - Get rid of secrets - Scan your image for vulnerabilities - Clean npm cache - A generic list of ideas - Last: Lint your dockefile \ No newline at end of file From bc93b1a42075d0036289432d9be68dc073810b37 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 19 Aug 2020 12:14:04 +0300 Subject: [PATCH 0367/1795] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fb6349c47..482dbefab 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines # Latest Best Practices and News -- **:tada: Node.js best practices reached 40k stars**: Thank you to each and every contributor who helped turning this project into what it is today! We've got lots of plans for the time ahead, as we expand our ever-growing list of Node.js best practices even further. +- **:tada: Node.js best practices reached 50k stars**: Thank you to each and every contributor who helped turning this project into what it is today! We've got lots of plans for the time ahead, as we expand our ever-growing list of Node.js best practices even further. -- **:rocket: Two New Best Practices**: We've been working hard on two new best practices, a section about [using npm ci](https://github.com/goldbergyoni/nodebestpractices#-519-install-your-packages-with-npm-ci) to preview the dependency state in production environments and another on [testing your middlewares in isolation](https://github.com/goldbergyoni/nodebestpractices#-413-test-your-middlewares-in-isolation) +- **:rocket: Two New Best Practices**: Yoni Goldberg from our team participated in the last JS Party Podcast (Very cool!) episode to speak about Node.js best practices, [🎧 listen here](https://changelog.com/jsparty/139) -- **:whale: Node.js + Docker best practices**: We've opened a [call for ideas](https://github.com/goldbergyoni/nodebestpractices/issues/620) to collect best practices related to running dockerized Node.js applications. If you've got any further best practices, don't hesitate to join the conversation! +- **:whale: Node.js + Docker best practices**: We've just release the Docker with Node.js section which includes 15 good practices

    From 55fe8d6fbe9a1fe1ded30a62d2e5e5d5bedb7673 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 19 Aug 2020 13:50:47 +0300 Subject: [PATCH 0368/1795] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 482dbefab..b8dd5626f 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines - **:tada: Node.js best practices reached 50k stars**: Thank you to each and every contributor who helped turning this project into what it is today! We've got lots of plans for the time ahead, as we expand our ever-growing list of Node.js best practices even further. -- **:rocket: Two New Best Practices**: Yoni Goldberg from our team participated in the last JS Party Podcast (Very cool!) episode to speak about Node.js best practices, [🎧 listen here](https://changelog.com/jsparty/139) +- **🎧 Podcast**: Yoni Goldberg from our team participated in the last JS Party Podcast (Very cool one!) episode to speak about Node.js best practices, [🎧 listen here](https://changelog.com/jsparty/139) -- **:whale: Node.js + Docker best practices**: We've just release the Docker with Node.js section which includes 15 good practices +- **:whale: Node.js + Docker best practices**: We've just release the Docker with Node.js section which includes 15 bullets about better coding techqniues with Docker

    From 93faa68c27d52b40f8b61bf50e4cc4d091874501 Mon Sep 17 00:00:00 2001 From: Andreo Vieira Date: Wed, 19 Aug 2020 10:41:34 -0300 Subject: [PATCH 0369/1795] Fix .dockerignore section link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8dd5626f..0e9a0d12f 100644 --- a/README.md +++ b/README.md @@ -1117,7 +1117,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **Otherwise**: Common personal secret files like .env, .aws and .npmrc will be shared with anybody with access to the image (e.g. Docker repository) -🔗 [**Read More: Use .dockerignore**](/sections/docker/lint-dockerfile.md) +🔗 [**Read More: Use .dockerignore**](/sections/docker/docker-ignore.md)


    From 0ecff6fc6abe54c445e50a6f1c1f97ae63f62698 Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Wed, 19 Aug 2020 18:48:44 +0200 Subject: [PATCH 0370/1795] feat(docker): add multiple examples for multi-stage builds --- sections/docker/multi_stage_builds.md | 47 ++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md index 42217e042..e3ee692e7 100644 --- a/sections/docker/multi_stage_builds.md +++ b/sections/docker/multi_stage_builds.md @@ -29,7 +29,52 @@ node_modules docs ``` -Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage and then install production dependencies. +#### Dockerfile with multiple stages + +```dockerfile +FROM node:14.4.0 AS build + +COPY --chown=node:node . . +RUN yarn install && yarn build + +FROM node:14.4.0 + +USER node +EXPOSE 8080 + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ +RUN yarn install --production + +CMD [ "node", "dist/app.js" ] +``` + +#### Dockerfile with multiple stages and different base images + +```dockerfile +FROM node:14.4.0 AS build + +COPY --chown=node:node . . +RUN yarn install && yarn build + +# This will use a minimal base image for the runtime +FROM node:14.4.0-alpine + +USER node +EXPOSE 8080 + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ +RUN yarn install --production + +CMD [ "node", "dist/app.js" ] +``` + +#### Full Dockerfile with multiple stages and different base images + +Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image, +and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage, +and then install production dependencies. ```dockerfile # Start with fully-featured Node.js base image From 7a99ecccafeccba9f8776f3a82dc96399ceaf4e4 Mon Sep 17 00:00:00 2001 From: Bruno Scheufler Date: Wed, 19 Aug 2020 18:49:06 +0200 Subject: [PATCH 0371/1795] feat(readme): add simple multi-stage dockerfile for multi stage builds --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index b8dd5626f..2a9abc23c 100644 --- a/README.md +++ b/README.md @@ -1087,6 +1087,25 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. +### Example Dockerfile for multi-stage builds + +```dockerfile +FROM node:14.4.0 AS build + +COPY . . +RUN npm install && npm run build + +FROM node:14.4.0 + +USER node +EXPOSE 8080 + +COPY --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/package-lock.json ./ +RUN npm install --production + +CMD [ "node", "dist/app.js" ] +``` + 🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md)


    From 1692d861d462945b9f463baebdb59479371cb5bf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 05:53:45 +0000 Subject: [PATCH 0372/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a9abc23c..19aecdbfc 100644 --- a/README.md +++ b/README.md @@ -1497,6 +1497,7 @@ Thanks goes to these wonderful people who have contributed to this repository!

    Or Bin

    🖋 +
    Andreo Vieira

    🖋 From 412886c3af656479df27fb88b30d9af04d79fb4b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 20 Aug 2020 05:53:47 +0000 Subject: [PATCH 0373/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 4e26b3f55..d9fc5005c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -960,6 +960,15 @@ "contributions": [ "content" ] + }, + { + "login": "andreoav", + "name": "Andreo Vieira", + "avatar_url": "https://avatars2.githubusercontent.com/u/508827?v=4", + "profile": "https://twitter.com/andreoav07", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From dbf75b10a9fbb67758305c0568c1dd7c6f77421c Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Aug 2020 09:55:08 +0300 Subject: [PATCH 0374/1795] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 72f0aef1b..2e5e35218 100644 --- a/README.md +++ b/README.md @@ -1095,7 +1095,7 @@ FROM node:14.4.0 AS build COPY . . RUN npm install && npm run build -FROM node:14.4.0 +FROM node:slim-14.4.0 USER node EXPOSE 8080 @@ -1112,9 +1112,9 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.2. Bootstrap using 'node' command, avoid npm start -**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts. This prevents problems with child-process, signal handling, graceful shutdown and unnecessary processes. +**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-process, signal handling, graceful shutdown and having processes. -**Otherwise:** When no signals are passed in you'll have hard shutdowns, possibly losing current requests and/or data. +**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data. [**Read More: Bootstrap container using node command, avoid npm start**](/sections/docker/bootstrap-using-node.md) From dcc3eba96717833fb5bdf5e78d574315b672bebd Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Aug 2020 09:58:03 +0300 Subject: [PATCH 0375/1795] Update bootstrap-using-node.md --- sections/docker/bootstrap-using-node.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/sections/docker/bootstrap-using-node.md b/sections/docker/bootstrap-using-node.md index be2e1d9ba..283fd3c19 100644 --- a/sections/docker/bootstrap-using-node.md +++ b/sections/docker/bootstrap-using-node.md @@ -4,7 +4,22 @@ We are used to see code examples where folks start their app using `CMD 'npm start'`. This is a bad practice. The `npm` binary will not forward signals to your app which prevents graceful shutdown (see [/sections/docker/graceful-shutdown.md]). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. `npm start` also results in having an extra process for no benefit. To start you app use `CMD ['node','server.js']`. If your app spawns child-processes also use `TINI` as an entrypoint. -### Code example +### Code example - Bootsraping using Node + +```dockerfile + +FROM node:12-slim AS build + + +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +CMD ["node", "server.js"] +``` + + +### Code example - Using Tiny as entrypoint ```dockerfile From b9697e1b73d5a8fcb064cd93dec43e0341edd957 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Aug 2020 10:09:16 +0300 Subject: [PATCH 0376/1795] Update install-for-production.md --- sections/docker/install-for-production.md | 31 +++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md index eecbf4080..d6cead3aa 100644 --- a/sections/docker/install-for-production.md +++ b/sections/docker/install-for-production.md @@ -25,6 +25,37 @@ RUN npm ci --production && npm clean cache --force

    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

  • ^+OWzB%^1bXO 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%0A=N;m6g z1=>kspYaqSn*mf4ve%bfn+#@IuinY}?E|zlgn>p=%1ep^|AhIVKlOe9^Bg~z3Bfgd zubPat0B$xQ)G_Z`1#QJCW2I?hjXrls;5oUW@AxnE0n>Zrz_|oSm^8J5uwsEW{RV)u z2Dr6J-Z^9J!q*c9{8GzK=56UGI%|Lg{K1B&y%!?iY$kFHIEe# zDHHPuVB%-`rYU8W^`hoC_9C9q-;^SvP4Vi`R`(h=)rh>uwGi%cpPC}~DsxeV-=yud zXbB%>x#hWUrhZZe_wvs>;+mf#dKxEVe~s~o%U+2O_JPtP%=Etwjr^r{C3{NWU#H_1 z_283QqRa0R|Oeh4GbfA`D3@}!X;{}8JBfvVVtDfzie94Uj5 zR=O=XNW@73XVeqb=5g>{;wl;CrXWs!l<*l9Oolzop`cMo1up1tGk{29^5QcRI)5c& zG9kgR2ce`6PjXm)a$baJOKqX1aY;h%y>U;v&kG^JiuQ0pq8y*Mk|s`Rjuv15G8{zP zGPuVhzEh&#CEDpaA9j^c-p-NHdv@V1eKyLldH!&p-`&KbWZG(*6|~B5scnj1xJfsj z)JzG!S`@z1Pwk#_Hcgp7H!Yq_V@!K_3dWfd*_8YP3yJar#tnOHC9LMJFgco%%xnBg zE-$Y!(ZZjCA0_Z1k3&q>LfYQPj$CFtXQOKob!bkw)e3$f9nQHyKl$|AggVBv1gY*~ z(SnIL{|O9l2Z<>R%mM(iBF99V5U%|rOiAE=*D$gxob6XIy-McH8m}bM%<^k|BnlWO z`&46N%>hiBZyD;=u%{-_(*%qltWGA35V=BNJZFxFW*+!S$b>HeM~*`b4SlX@BaW6t z2X1o-7!j$_qI_;B01_KaJxnTSM5#H9Xo16Z1?OhR-10Py3qKadX^Dh*ZM1)*x9E< zY|3$PHk2`p>Krq&2u7eOqAVSZx0;R$W_bES7=k&+KSpNfRuGDbl}T1zuT6kbB;@2t z8Ld6$yn{vn{s?mzKpom4&1B}R566{Z4KQe^*FM^e0sd$c6V71U!!KfwoQd3yg5Ga5%^O##V>rnUHzIb ztS{-Sdh~EO{@L?6GGK^?)QCf)8-a%{4~+nylz2;4cKr$B9JkA4vkL@alNse1sIR1b zM=%|L@KfO)&m|)Fd{iXbN%{hTmBM;(Z`uI-oQR*Nk&OkjZ-`^Tg+ww4C?BoY4a@Hm zaRE3E8ubGxqD*R#{0ES+KAsP7WiRj4X_GBMAC+fyQI_TTY-~`pdq>=ZcvFDS zczZ z6%3!69>&H#wTNmZb5Ms#9|E#;((j`+sKNx8>~U_iE42pnTV7ZR;sW0j6VBD?iwsIg z*^R)3*%sa69QX6F(uSS! zGcuJyZK`Ny8c6d6Hbyz~O(+=Kl3$&?1!avc^GmznYLMD%mL*^UB#}R4i6qg!=>t+l zVy{M~2Itv500iKaZ1kl@XCHMymwe2yNk#`viCe5_6fRE@ohww2c_A z4j`b-pd(Ocyr~=J)ir5um>eKi@_kA)up_i{nh7ek&j|w123Q&4Z3;lWVeGq%TZxpG z<%XmmXL>+wn4!U3n>4bk8C0DyB>b+)OWSA#>VSfSE)mbjUozDk5oL{InJe(%eKUl2 z2*P1r+i0H5#2${u^v$G>w=&c{faNuj$u3~>No-kzsUM*vym)k*k$PE?N14S zuWfo<2aup$>k;W{&Dy#pw04KNS`Emx!dx^Ch-wBnt0Pd*X&h!2ZqmIg?uz|5Xs zU6A1NyV=406QuF1ceJrJz;g|Ay2nHW%Sf~-NR?^d0KohB;NeHj7hf(2pUds+P}#j6j#{^V_vfU_laURpAsF7kodksYS!9-ga&I+O;Chd z2e1}sO_%t$?HwUfAOhQlNM$qTwgbJ&(V&>OXhLF3qrm<8U;o?L@$dO!_SQYd7!btk z$$|&G(AU@qkl<7hUikjYFDFJ_KuL%PXikfDw&XpiVV=5&hPlnTDnL;UhmQFsIMw7$ zO$zVBTnMtV@M@Ecc+}vCnWp?TKb#0v~BW z6V;5g#Kg9-@A3R@H;6Vk{6)-NZ+0iQO?Z|5(By5up10~Z^ZaXnVOspczuRg2N6*i``Sf{= zfE>e{<8vH=|LS`FuYB08{xPfAzMG$8ZU6s^?!YvB{Srwwo9t5QG_8%OOb|LbjKrKn zbc2zHSVN^eml$%1s04gR*d&e;s>E;$VuH|ZRlZz_XOosgq9qxSz`6NLy!qrQ&cz{t zv>kl44(kpA%jQiroRn}TqMzD8{hYUqM3}vWFH%;@a#OMm9;pUC$G`14i76j<03-;t z_j2Bv0+iThD)9;H2mM9(CF%@32397U1x^8$&L#30Z@OPr?+ju`>E> z*r`n``^-;B;%qzvP{OR2Twk#9Ys#U)g8n4%x!DXLF~V+Ne-1Ybj4t32*(4G9%0%hl z-G#w9efRC`_`y4Do&q22((x~t%yH6%p{e3HUdSlZ?iuT2uK5%uAfs!{J&=Ste zD?{SazcA%+zU@BJ=?!4ZS;%1^%y#W&tAB%J{Xt^F>C zg1!fuL@|~y)l33O4?$airzIRPCBZDobilB~OwZ}Jj;gMl+)&2mX`oKS>gp&S8W|X; zCK7qqqvK-4Dw&>DXnpWkz@)53r*y_HYXmHSdEaXu(r)}3lsNOe{t;_7Zo^6!{B5ft z5~#qHIfttVo}iWoP>C7Ql3zlHWo;r@)c_U^K!l2m0x7;e0}IxdAf-h)Li}7t&TRBB zk#)+@Q6DZA0P9$zU7^pK;&d646=Pjs&NeXL2~zQbKIbsbEBzB-*w&LY?v`x5ZM0QQ zfh5Dc&|@2>x5@mn`ogJRaP1rk0yQbi@Fl1KNc?FM!K_Zck1BwtNutd<0Cq#8lomH( ziZ_@8nm1_8+{8OW+hZkcX+R23+sy92{dQJ200EgFhc}a8aEVO-m?O#)yIq~}$5jN4 zBX##5WqtZt!$hf#+20eKyT@uVn76B|$AE(SgywyhHjtTp{4}co4iEQXT$x|{Wuz}d z3-ggV;&ooFQ?ud@yS-#X`4(RAoU-Q4gl!JN-&RF2tXo9Jx|e0@fQ9bPdv zJsA8E8of_3ztT^v4fvrTpeZ1TaSsL;0>N98tx1SEJ?@8wYdF0kq8>p5Sof*}4vEs& z$26$cTcY+8DsS&+`ClMs~(KuBLVZkSdPQ zV3AO>zQ7+ynnk~I0$`K?A|v<-#1E-^M!5rk^@woYZTyflz&!5X&o0o6z?u^_7=VaC zLABH&{zT>nn$!W~X#3V^Vi|zQKSSJX1Q(1SYa~oeSM-TIQUb~afbhHj;5V|9{;y`O ze;Vx_VW*G2@OIWj!$h;8U91b}VeA9GSrSudzY0DD7rxm|Xl57sS0LQcAMFV9whI{L zxxpN`r^cFG0sJXQyccsjXU<2^8OsAF2AHx*PZ87$dANwx<^FTZ+ZQ;gkEuLpwY-2RWYf>z}^Ni)itv=7;XUpB4 z^Q?FKJolE>A%#=ZJ+Km>^JJwKlT^r^S}9T zw_5-6ozrhVzT^>j16W@2fZi?cPhLzOKJL{13dC}^&hK7+Wj?oH#xw(Kv6%<#fWSld zIAr)z(6*rKy(vekj>{lzI$krq}0u8GCZ~?`Bb#!!Y_q;ZJvy&n^VqnmXQDxJ#(jWNUX~n{mfyx9;qAjgYQne_lM&q?Mopp-RNNa zC{w#-uesR?)I4O&Jv_H$<1z%XnHI&P*b5~k_*p1ZKFig zDY9>jWr5$tEl$W?qK1`|3KCvy@Nq7mATjR1xUH7>fs6=?i&=|NfHW^$!ZKlo19NwpQ&%n%!npM%W&F;;nkiTCW4Lua^yx? z1Jt4K2m{9WrSTHjm^ofhCxgR)ArYrf++XEa2XV8!Bl{1~GROP`B>@h~6vK!|h+TjQ zjX;Y7;6O7Yv!l75?1z!o>X@6AILo)-ik0jdhFP0IY|o4cj^E|&1yD;z5UFa!Z!0bpkt!Q=yi&CkMnRPz1|(XcEiPexym z-Qw3U%r(lX5L&y_ILOBND1dbpO-CgXH^MLr3R&mVHDDAoD#L9X(7_PnGd>qAN6n`W zDewC164NS^Vpg)#!-rY@5U^)YV7#51qK$tG<8Vl*<`LP(7tn?=Eq(6LM-~t?3@sXKN}rS&8mt@>)5H9Lx}h@_{9piW5p;n74V{8-}>8Gf4UCrMd5y99{*_<|7F2x5?NByEhC z8?AN}%KvImcKcB5Pt@tV=Xv)S+o9iQD6sC%oNeIE55Wq9F4ZByZuULkU!pQnz+aBu`?5L@T zTBP`KnFskOvGkeUp|-~EcrKhH<8xw0WbO>-DE}+`iGEv#gxNA}jDH;@e(jqdrR&KA zjkdDc&?2kg+HT`J&H@0x+sU_h(bEJ z!jpO)0Z7-eSF#D?+@TF13LK@!oI>i0oxQmjO#G zP7))y=|>&4L02esiU?@s7Ar%;s8e-Qab27j`z}z7^ zrW;z;?C6?JZ7@v>eTB9tGCIr&*a_wZsrMAd+pCNXycOmw_$a{X*U(H@=aPs|3c(jJ zkPDc?F*$QI9lC*$slf!-C>xVz_JkY9i2sj5_`&dfd1c#~?dBkT!tAikc9ZzXsBAeSR7Mm^RNfsecS}?8{q$ zp@QijE&Os?aDgqrRt4a))C39>BASwQm|uf1$b_wZv1Hz8-U2O!@0wBf zXdUl+1FhB)voZuP9ZVBp$kp<8Szm^%)xT*@q^c>77ltV=087_Ixst&pPbX`ES);uz zOrwe`)(<`+6CzXHWEWYleM+DSB3=PfXK1cMYk>wqQ#04{3RUI|txAAD*76i>iV@LT z0EJouz`~lI0cZ^2dqR#Q&A9|C0`&oj8yA>6t~GJfWD-rJeoAU0>ddp@#Z9(o0Erg) zLj-%sC9<0p0WJV%g3Bf*VbdXi4nTX@L!)y1c6LsZSb?>E5c`1W1!pQSt*zk1tOy}obu6}RMfsQ zk!?)GO2$Dg62vS_)SA$NDfA^bx*XTWjHCWXHP-&sQ;$)+1AU4f`)N}2 zBi+Oq<{@6Cc*Z*Y;ilMQ;yY!=tIZifh&g8HqIYq`-SOG7-CL4RD(5}1J#?D)hB09?O04e$NI@+LqzHcgx!ryDjs1JyAdXxqF;2f4@&n3+8hu@?V={Nc5Oat@(Gp zvDv))4N>NssBt}qE!%2%0g&n2dx6W-!4$4<-Owa^+s^uk%5zsc0;8LMv*sSKp4mR=&2wGIir z#LjxcR|YD0m+-~r6SbvkgFw?xrE>{D@H8Mp-vx=Tn?h(*wglFu%J6bJ%J=$?`0o1T zHJ|Z&sMYx%t#qg0pJqmW@L3+_uPSrfEjaVM4yG`ZwB0fqZ2F;T;(0eNW92LHV*ihZ z1QRc{x8L@jdt~kebGwZqMO|^z^EO&z-7@^tt#hqf2-~_evo@0Z4Gi}}2`VN%IUBr@ zljbm{<~J~$ZwF?ok4f0k@i9r@&~g~%h%)t~sACo))2xGb9jVt$tX!O{&kp^DgG$-za$ym2Ig{wS=h#? zVRXerjAi(s5H0{lWdZO|dQP*MEXE6_(s?P6M4RxfjZHm<7BF22T&rcNVEQGFXu{xC zU@T@hhr?b3!|&1!a7ZL3p_>XlCHN+1EE%Kl`=KEvzhRaEY zj+jhvg@d>v2~G4gN0EtScwm@eU>Ii^`Ou=#4}p)-q6(ad1t6B8zsypA04i><2HK1- zqTXA81Q#}_9~(<_vW8#C2HMp*!08+_JjZasI%?wg)Fs6A(cyj8BN4?|SB7ow zvsT78%%^LYxip-w?_`tcd~e_D6OtS)C*i86MEN5wHo#FpFEh=EqeX!_F`sBx%Vu_k zk5IWsl1IX1ckr*d90Mp2PO!W~E7iyId@z6xP?ws(4Vtb2&nyXopqA3)sJ$arUJ1yc z{uX(ES}^(sNzfee5HK;fozg1C>E`MQ^o4Z?cs6K)4(RA!S>x2nOJ=0L75+0D)?oL9 zwGX&iu!ai2j_bkj;*T%Rh&I;EF0VhzzA*hsv_UDNo@RuajwY7Tw;@CpXk&Pn5L$H6 zQh8RhQA4ns`z7{2KyPTYnHK{$sBx{c<}{7gysN`|c1Hf35o6tenXYk^qZDEJL02!DQDL05@N~FuZuht#X5PE~jfec~!*`CIg}~kY z)XTfR`Z@Ml`?~$?Igedg@oYMgJ)qD32Jh;B&sfTFys}4h=6UO$=9PyZ6sy0+ncwsL z?3+(7YXsf^mX|e>cgp!shl@Xc*35tY&Z$@O<5#a2**zRL-AuJzhl>e%B;zZQFp*U- z;4lX=`JsJ*i4F!GCMrlRge3?o1T3^Ie3$U3t)RGIb~L{dSXj8%f?4-DekAZxumo@* zs6hbukLy9;wscVmy(Cx@s9n9$HttJ6NwO~p*-~xEHP2e#4t#iB%pr4=D)M^*5z#I_ zTjx%?B-pZVW{9^|ZL+;joM2xZBgtIyGv@ln$Af@?^vt~!Y8pcbFNQ=5E z*$@U!aOHfs>8c?TfEkI)fe5Ap##|qsH2^~9!-!^%t)L=&8~_5EdBC*+0=19dXG{Td z+@r)-vm_ZUgE&;Q^`Z>NT~o0E_V=15Y0k62pQF(32?kyB2cr*(Dgs<^dX1?2q0k})qu?Lhln4Y zanW|DQ8Ks9KDkzEaJEx4S_Ua-;wvC?UFDqjH(;{VR)vi`*BwKb(bdf~J3AxK4d4zn z56mjzbpZ+H@tC2ys30h)06+|0AfqD?qAQFyeXQYEK%)`ZW0pm~nTk1)snkX;b+iYh zs97ik9Wd1TF|2r32on+cY6qv4n_UKPf;cXALwOh1phz!*lfjx^uU&NA_BB9nD6 zvnnt2vID7S!am@u=3V|dP*=yoX+CP>ZB8dJ9_$jE>RD?_%Tr8CiIAHR?#A$qUQ zo<?)8X|MOLA&D7!R&4g za2#O@Hb9fgY9KhkJtmJNjsX42`FXay)GQ9I9c|mRZ_&D-{Ti@d0dafy;S_Tm$XTm5 zH_!-tbJ`Dx3N|BzFkzB&LlhHQ*(heb%*-*1R81uBPcZ~|0%%KqbD{wHp3LZ{2`qbi znEv4xv!>h%8XD8r+>pm+jj*FnK!#Bp@XQ)BctQm_G6i@Rn246ldmZQZzCeQY7Gc-H z(VC7D{ta3Iz+VF$y5I|bWyyAVN#k>oQ zBY!EDFT9u3?HX%gf;qgsW!*xH_R##W4t+l%A*^Ce6@Aw=XXF~I-Tm<%YkWb_gq-(v z!#ac0vohmu0X%q_372dr@3V{r6njSG?$x&!6{>IF*X&X*|u=Mnt%>pE}f&q6tH=o^MfBYWLQJ}4faL=}Yvf|DdBgc+!^e0$cDvsxPz_vOt*5O9y zncZO-ZWR7;93x^F?bQz6`}r7cWFJjW(LS9=L;BvvGz?Q4c5E0fB+>ey0JyZ<*Q7?X zoi+??n~gXU_>r`)_f{`^=SSYjW>3lCF@$JffBr_PP z%S-Y|5ZUJhX01WQu{9xY)!r;%fGYTN%#i-e?AKs?44oPp9;EXaprNfn-H%xhvlZF` z8V6Wm9AW0F?DjL58U0S0IAUw!wuF)2z~sox82zY0W^%Oxbu=wUFsO7gK#C@I`tob0 zv#w#vf#g>0M+q|v@5}fxpUQG^MwWe(0d+hJY~@Yx7;6iTvlTuDj6Do}12n_T@jTb@ zy->r}g0l+ADkdW`s!PhzluEE-F$T2H{X96g~u;O$KbmA{y zM$n3O#2gAGAY%^0r{C3hIixIeSg=k&6tH4wmY5yp*T`$(C{Hcg$QUpZRZYZ_YxCba3w;=2yooY1atXF>7m*Xi>AfHK0bP zf(COUlV}+4DVm9JK*t?w_$eBqUbatmcUWQo5>faTXu%kp`IOA<0HXo(x@K&B%L|!ROq$wckvDFx z5#j{7O&I?g@4_7ajWNvrVhM;iAvV&Q+(&A*G#!LNHur~7&N_tNZSS?h?`CQ+6o6tE zrhdUVtualLVocF^ta+cBtv)<6>m0_;VVt1(m@ThDJLy_9l6LjxIFeu%q6kiO>C=S% zO#rV(=W63OVtyIYYGT&NdM4^yF=f3G1Yj-Lhm+q2Ja8c!kQiq)y3P*pO}dBA(e>c_ zL@*{)bJ@!ZfNl4HU_0D_ zN@ZJHs*1nc%D8WL|L*T+9({H_)zAIgb~`A}3;**-YQI-4XIIbfX1}>%?fsuIuAeRW z75Q%S8IHThN43hI_~vx^zyHxr{r|Xo{>|qfYy{o_mLF{VUU>RHzPRWUdjFTpP$#@_ z>6QBO5C_8FId~3MY+Nw?{*|GZiUe7ao%C~hA0sgql*Evt-V$IX?qw^GXmg}zA(#?b zUg8~~@qLy^3Tz}s!H~xp&ptl{AmM~4sNr-9Z45CJAjEf>N)CxyoDmdoos&T@xz7zk z0_Zfz@awRzw0h?+6WSz2S=(oJ&uU^4?T&ifR3xYjhz*V%0J9Or(VQ1}*p48!mP3Et zgd7+dZ(ESGO6i{P!H71ecG^nIwhWI%v{O|{L~EI;466??wd*l`QnO(0iYWlY2vRfb z{&RSw8H}+bGCaSH90%qkStA98ZR0F;IwnZvoA^9bk!~uL-8=1M2Pb6ThPnIxqsK&r zB6%GBXa*A}BTeoH!W-Aff?fbbN=-p9i%}w3#p$)S#{L|JUt9Y=Z8k@Nre^|-1;8OE z;!}i!Ra1gQw5k)btVC!=2$gDH1ehevw+8rhXi%Y+MH}|m+^9cqQBG(OU@ib94su;T zCm4+>3|yPf=4x?mg2ezUu=#)#fslZ$glSsP_9fEznhYzFZ@nKjfCbo1OlOpwGtR7w zI@07=TLLCEQ2?6-(r1OtbtL#X_tsdaEAG_^cmbH2!9=?*crc{;OV`bue!v7ZX`>;G zXLFcY7`PRY3o5i%zZMff%KXzx7-2pGE@bpI^Ksp2at15QT7rQ>E44?a@e)RTLVaz3 z;9Q_X9}UBLh#62126C>1oV*${@{F_qd<4v;ZoouA#(D0`VdysW$Hf;pBlyegu?hi( zFpvf$(5GU96x+NX0w)=AwNq-?+JHnQ*Srms5AfvOH>CCyb1v6>B+O(mjVw3keJRm2 zwfguNz?fI);~J)4GnH!fFeK!2#zdgJ!AwdA^*UxnHT-oJfU5Bnt;T@%5lXjp^bkNb z&yFY5eF;#+VLQXW!tly{w0Z^FjS`T$;T@O*B8cZi)Ghowm>1vu8eCxg(1>X;%Op{B z-n)cP?hv-OPEZIi8S^XsVazs|f*C5BerDuAX*cd=WzQT%IMkC)Yw3Fjm{eyt(cgx( ztIrPxi~KM*CWq|rp;6&o1fbNJs}7nQ{a=>&$jr$5(%^k5&@e90o|$Yhz!v6G4-X!~ zG!vO@{)l%q&wBW-m}ANDEztlOq04AuGqpY>#WZQ-ig#-XurJZR2s~@N_XfEr>CXl; z&qp6V0^oBGoiw;egStp5tt}r zm=da+kn1A%Dq255gD5tHO=vW|*Y~cDIc!l*SZ4$lVC^)eL$tL+zYQzB@mbzqUPWjm zYXV@RStgQ*?2F=i=55aX4c?U)^wrX>f7c1z4T+@oSoi%rMtP&jGD z_OCQr`Yqj~2p^8}{21C5E4p*|%{GL_f-ioKHa)wC=P4=no87bA7iB-Y$FlEy;co8; z8W^W-gZ)Z3?f!ac_j$MP$L+txf9XCePG!X{yGBH@j)Qf2(($nDG-m%I^Ju8!e-wLg zTza+sOT+R)o6zu8hPCXM;R)aRPfkxERBw)#F#>M@%gY$WZ5apc{a?AR$u_>a$05$J zKY0mr6+}t;v85UkBYp)Kp^1QSz)J=Bkl1)%5EQ7e-(}9dSCcM5fsDN81p}JmSibLZ z5K8Mv|3WGjgq(}vpeRw=HbIG5NSwCB+|!onp+>}Gdn?b%G(p}eYz-KROut+WwK|(q z^8ZLJNKlH0<7*;`aa{sw-}td~3BA?Xm*7455@vC3{66kYF(!7#o^dZ_*xQh5*)jXd zkpRZ_Gv8w)p#g4ILNV*%PqZoe$cf!tI0kW%;v9<5OnDuqRbP;V&XQDJrri*wE0{MW z`sQ|+Bf%Wt?0P%8$_AMFD7kfF zc9FmjMzcqIOPE{1mN^MpNX}|`rIG~g@aYm$(1r> zy7d8AF+lLugofoW(se#EXCMz5EKRfkLYl;>#gL)XRBVIOiJ8448W7`aG^RB6%sUL7 z&iAVTMbv{+v&kMubNnM<_WLY?22xYlJ^&GZHZtFgIV@|MPS%AIbrY`|9=yi*Ib{M4 z87R%pWV8&!Tf+FXFk$K7z&&El%v8Pt9O&~RGhIg_UkBjp{OjV=6l+F*3e3j}z>jOQ zh9tYf-!*Nn!C==YYlT@*0YC|}r~{LYjZonFl6{XYmJufy*&d za$GlPmgZ{L7=PD2|5FE1t5^s2*PjwCkW%IbbD=nh;TO`x z$7vps+F*=b2Qag&Oy&d=HcY@O0O_1HAzd)q7`6zLlECP;ax)rVdS=3>aLQ*B`1=ZV{NXR6<}y^ zd%>KLAE{rUc{$GZ$sMEh3iWbM2*jap&($KOHS@qyCr!1O}SL!RAR#IkPYQGK#w1wd;z{-dcstV zw(=-f)Rf4A?Ldm3(uN_E;4@0Y1*>AQu*=H;MKL8$u~JEeqCl1w0D!!F4fvdJn5hoM`N z=n+npt*?!A9VtGytT9_z7-@@gR!FZkQPJE)sk&iVa~P*CAZ7IEX?A^lhQ0b8WneZ0 z!?_{4lYLqt`5fcJV&>@*lbKtkxk!6+7++17a7jR#jJXaBvpym7>40FmtLzA8*%Bnd z48WKGY5*+F4s1QZs6jir_;M^^Vl)XV04Oue!&ca&8@|`6gkON+Ve^K`Z?Zn@djrX> zPO&Dms{;U(7z+%VMwfz7)-S=xTMZZ<{d-mq{V9 zC}46cXeV?6H@q`E8@W(m)M4zs9zqG$r%av!3zUoxVT3iEsqqdi(OQ|<)8wLtq^`RL zF!fPluC%@?kR-nL5~et-9$+d2mW-%$%qOOYqPaPJnb2;f+g(iNdPJdW!AxIWqdfym z8w{eR&ji<7-3MqFVAk_{+1p>nH0Z;RvRRXyDrlj4`y@4da7dd0dbFoTlG7Pcw~he` zbHeQ|$>MJGE5cJT)EGiH0%-1H|HH* zO{&@LrK_gV9&n+2$!0d?r7Ocww>sEiF zHOzjUK9c2>QB_-tuL-7pZG;u=`}>Rs{y6~V89qRVO~4`dcGZ%ysII6>U!fm+=L>)+ zz%1Gz6SNwP!gr`ozym{(>*J;-a}N-z*`*}#7^is?mbQC@*EYxm@AiPO-z%eNAU#7- z%X*_cvf9_EvkrKu7iuv{MSFFHummltAX|C>y{=K8K2(~8rcmI_4FUws4;!>s&0fa+ zE7qT3vm*&Dbfg5am%JBMBM#srrGMIjwg1WRoc5wo#w2pd8sp80HVUvM*HSz83b&24 z7qY;Do=f++L84Db6<+x<_JBC&Y3MD5#NI>u!ep6u*Ms7?A(fqW5YMM;=@R#Qi*<3| z?wtFldtL0saWyVIyT>BpZUGX9rC7gZZ;N?$?+!e~jsGwIMZLQMw)e*KaW2)h10`Oz zjnAL)+pc$89)s`fUfI71jsElOUALbpn@g|k@hH!K{LdA|H~(Ox@kd|TM!rT3djnWr z!eo8(YX0+|wyS@{S22-`mr&?SE}{-G{O5?HPp}snDZ@-_C1^6j5CFf+oCI;(5`-YW z-XA1I!pVKy6H*@~(6BUNBE=_xj9QDOrdO1uQ@6~vW@L#3d5@nG^BKTLsWaE(9`5yX zfELP=*^jEX|EKmTZL$?y=U&HKO^?@}x0`ZcqeN=meZAd}C^iuO}Z zlod}#>CrFCmV>s=FlpnuZRG@vAEk0Re1JH$GkM#U7h_8;_L1VZ*VB002G2!XsUsw7 zlx@>3-fE*?p5sPMu>5Pz3$x3c=cWpCAk)4^T1aB}Y%m;W!|Nd-ZjqW|YNAhtj=;$C zBPx$3DTe>mR$S>|$FQ$35A)kkBT3sDlbs=U=+{ps$f0qgrV(Lp4dXKTG5>+7gPG~Th?SUT5x2?1u>eFh)eOJ@R+WAtonB!+ zGlw}agZdJNd<~FjW2R#kakWOQB%&lCL5K0HGB9fm&}B&AJ=-ul%Hq}1z)gqd#jN&f z7#5g4PHtI`NX^xBV9)|1rm2%48fRSEu1^Up5ECPq6C8J0V+p{3a?l##LxM!PDs#>l zEGTD8K9IC~%PJlr}aa!ua@=M!)g z8V1@8*5O^k499uGzoi0rTI(Rp8MPQIKj+Lb%qEPP;MH)hQ^vCj^Cut=?FHX$kEUXV&Sd~$gz+*`Wk^56q>y=Zz64}*0K^Jr z)%U!QnUm{F=BkBM-AG(&6Eu-S4;5f;4Ir(eZJ3bwueutBhA@W-oD!mzcvIQ_-U%JY z?}qwJZa4>MO#nEZgZo72LUT1XS{!{P1aa%=y=)52sB|jX+XwfUe?nHT2*JHFb2-Tp zVJ3U0CuF3DAszrWSwA)UchtR?^#HHy$%in!`YE(wjLC(A#NQ}dCPp5(oYXjCP&O z^CotkkOAI!N5wH~Tae6{86JAAc_8niCW#%)<~ofo@1Y-g=7=_=J!qSj7Ok zA(Pu=96L=kMD%@(GhG(|y8?u_aI`9OU zJp0kjd!czL80Ug_jJHQp&bz~}pE>**0xLiBetCXp;v`Mq?KAEZP&t~%JzL~!ob^3JNy8G+J?R`EQcl+P=y3bJdZg1Uves){x z<+HEP`eg$+c6(*I6#xK007*naRNr@>{Tl+fJ2iRE!+W0{HYC12`w*A9wB`3hV+Aeq zJGVc3?iKj(VvAFQKCD##$S=;9|Lecgs{i#@poy<_6W#!pA8`8qv&X}et8VSDB6z#| z%Kd<)zTC$Z)O-!IfDTfP_YKnFm3d6KKF8c-j--CUMr|Y`eKSg(w2_iFl&BLXG$&Hd zDU#hbbzVJsoNW#O1~8B1Z7Vw`WN7~s#)a^?YAi-bgU8syn$Tok8X7<*n044cAK=eTzSBZnyrjBStH5QaFeE6J3Rh9O*Gp3{V3 z&o#NB&xQi7a!wNtlQGtiylPutfhnqR-tePy{9YECk-@Yn9ad86npq<0H|%O?gUB#0 zaMdhi4P!179XtZkZMj2q2rdJB7+|FMhJ`(4XdZ?J2(YjMkqC4np{e;$VA!XWzsBJ_ zSRCg@P(#^Eq_a8N3N<`A<6niTnG`VA)UW$Kg~goK*?3KM*Tyb?gPBgrm{tG=1|)F8 z1wd+?;N~N;kq@;=1X!0L%(Gf!$!T`w#hm|_xL&aeHzcgC6d zuIlJRx%%fc48XvT{U~`KP0rY%9d$vCQPsG=MxX}AsfwS2p`%MQks86dE{)d4G{fX` z3-4$RMvkXq&PxD`QNwcl6~?PeqG|Q{oW@bGs+P`B)*Va$=~;G8umCV6CR&;{AqjUJ z)xP0BHM+S*BiAJCG2;vCd-AY{pU6pew)_}o6_YZ6Qe=c@d~(d6CYVJnmm^G^zCih> z*>C*X-(}A5^}_Gv=-we^lSgI>*hQl;U7Rz|m_ov+H+$r?!NjuG0PJBFwSti!Vh*{d z4^0)fbo@^o!v+ac=d9ZWO#0qFCQ)5{LWrC|^@S=B#jHZ-8h=bMnKi2|Y~0$GsV0p9z8OJQ$!t zfL74|!xqu@Fh4XLvB3k52*)h_I3zh`o*6-nwxBIEN0BdkgL_UHgWf)Ml9$G0mC~se znnD8|Z1Cr50P?y$!#ytnQ=igi0u*36+JnX%92_z4Bo77XJUKIbv2{J*-RB*$&4i#< z6S5X0uol|@&zx~Fdf<$5uFyoat}s<)0j)8s%khuVL{hS<524y37Cm!nM6@P;e$d1b zf**jfzCvnA=gqag+TsG9%PKlga}?jL-(clpXS`Za3M8^H1dPS&d5`qgQb+rC{A*+f7f7F&W8Og6Wq#EOuQAZE|6Tb2x-y!X!B%kpHxc3>jPP}3sy z;TO6Y^(3Osvk-NORuEnOYOiln$nb~ZT4*P>L|Wh@lOVqE*wx`@RrWHYsUJ!|m8{u0 z*<>~}HaPc>=b%C6R3a`EW3yquwSCb}9@~|z8JE9)-yORR!kcZoLxAt(2)RQ7g)c0e!sL>(r*vmOD}7zS^I^p-snQy`cP zq@&nUp{ZV7V(8>M-F)`C&2qPc1J>oC;4?2@{3v=3?vIxtY? z+nADDran)go75Mfu(MBV~gjK;L0kQIPq15?|@6s?A|x`Ziiz?{qZDlyji-SDtW z+rUp96BkWM?6^S%*7VT@scDx?*#Z-sQq2VnR<h1~Z{50K##D2{gJ@NkuZ@@Q$2I^TVjw0_K+YmT^`KF`ivVV1=AM=PlL` zxoupNz*V$*hPO6EFslZ0vN;*zbEMxD{mB500!9-K=7e=N|4mLbt0tM!O~NR`nc$N# z04(eu3lMRn2WmjlipF}3#Hg&ng2bdyqA=~+KO#aF+KmyB%No!Es>=F0=mT;_j70%BZlitaxbD&N z85Vp6aAu%jxbZ*I%!}~{E&?*l2gJJ-QZ&{zAl|?PW|>#}Q^m)n0vMZ?*Vz#XM(g+s zHI@WL=(?6@JM*P4Q6v{7XOIyAn@vpS7`Hmw6r+X-B(eAkld>KfD#N9_!*m#*n(K>- zut4NF%+t`U5{M>iqX9cWBh=~K3wo$&={~eT&C!GVM`&d9eIcS70*Kl83P1wjK#O;J zg3~qTSo&_MRaCHX#CYxFq^}9DW~O7n{>8;*c1U1?&LJj%%xk@enPLdf0NSiGeePoK z(ckWh=w-ty!V^sISZ8(Cu)lLuqQ1WjG4BLEaO@pa$*8g}3KHgGlD4MrOdJlhNTwrkt&@9vuA zy?9tZ>+Jt={&}BbD7O!J+uiS;exLREFSEq{M`_+Gc-)=CUGp&5ZV_qlSNZ;>-MKe^ z|M5rQ4Pg1lAG2pq{Ha0l7d~!Pzxdf#^4Sfqz=t0juCn)VKvlDHmq-L*;@mThhHZr? z`m!ZP9_CNN!jC>FGD~lh=2@CYJTK^gVP!@!byDn$TLXk*r{VG=WcyAaRN=BPWh%W|yWbAG-%9&9Ht2udhzg!iYm$9Z-mnB5zGP2+J`yA{e`>)NQN=fS#Ai63PZSLW7h=c8sCxunwtfH$S6*8CBW2g zBFL7KT%2ClFwcFMFe5OXAc>smm%;U&)~QQWqXLtl5lm~Vg*gw7?8E6LfJf6T+6iEh z5pBT?Fhz_O=0KWHQ6?-egAH@3|-KLX#JPCn1P6@2LPV&4R8;)dJ>POY7m42cmU3Q}aFfVc+90S_H_`<&C~RGq3_ReSj6k>BtCerwmR z)7|d2rS#cHU90Nb-}=^ihv%K1_nF_!QR?18sCkBr(YTa4dq#@&>hKJ;M&HQFXokn! za?Ud@T{JIvBQej_h{@>iivDdfw`Ba<_5*F40f$TvTGV=EI2;|w$CDf5TJO?CR2ht*fke=Sb_GZ;#lHZ@f39`gzFEyw5>0(r;S z4%(+^m?1nJ{+Q~)sG`l3Nem&VNg$ezo=hj{mN?%#u5EpRnA;f5RTr2io#87nB*gI= zr}Q~a{{!Y-AE)*?OywFTUWf4eN2k>z4spGBv7bLg|2jYmit7jjHU1wsy?4-BiYG5% zUSbBzI7letfnQyJ$N{k<<^qg~W?)nNpgJ&3eKcp90dAZ`tPSVY7DsXC=8*Z_Lfg^C z8UNtH308T%YQ|xi2cCO^`If>V4+sS!V1!{y-~t(YLVgocd_!Fun5Y)!oCh!^9;~_{ z>cW;2<27lNp<81D*=0Vjn0uP(!fGU3xc+DNIm~mjev3H>E;*(gO!SOY*kle)5jNaA zR-4D1LPIowky`Iwz?34aVan(ybaHZpx!74WC)fDRAZv&QW=y!{?#mvUBYcGnBtY6G zjA_=m0uP=oF_oQPfX4{yKXZ}+DDk8 z=Gk=al3Sg^m>I#f3=6l^?Oss!C{2QE^Deb7HO?nx?)~k!is%_;%CQhX`|H+i{mAdz zey`1~6Ub)A;O7-gQ)x+E6yn^G^`#R6< zbg9`t`($;#+i8B7em8R1|A~<2u3dHOZZ#nwzSnGg-+#SW|Kh*hYyVq!4dzb^JZ~75 z-{(mE!n4J{{*MN&KV47G-)AxZShKxDRG=T=U@&lH5KFz_YlI|GDDk45y_yuzC7U^$ z8W^b%(`+&z2E)mEnGgJ5%Hp@k*+rSZg&Vm+YvxaVQrf{O)>e8eekglPXZ{)?6}7c6 zeH;mqQv{y6B{<1Ll9cA=A|U&#q$_1uc9<_-ccAumY${eByZ0<&ze=+QNOs8?oAk*9f)xjS{cpNx3#5kI5ZOaazAhl< zjCSUE4J*Rbjv$6TN2P_Mx{<2%1&OvmGZToihSJlm;a4@|qJH{tw}#M$NXCYU`3rM} z#G0ARII9tX$cWv#1P}xX{in~3{Vdb6is=-DC}p-FNHz=FUS@XIkgkcu~PhTdVdrl310gHhHHnlHIn{u8G2Tx`_LxeJ^fPlQ1-KgJF zla)~3nDrzqDkfsmWxmtQhbB@E54hT{9s1R9+5m+H;*jF>xmhr;T_;8yV=m~I1XwbR zO0J#1Mz)g~&~(g&BH;?piD@BbTa+>6Y>l?#NWXca`*WDAIc9@R`l&NyUg5(+rmBZY z)W-1LM@aLLvTLTbB(2Qf5+4s{CZ=7OzCq|uVSr_-y1Qe1A)XL+_q2NO&V#D|8$_Lg zfiX%}v~hIq84UXi&NQL<5avYe>YPy@<4IGEfVN>RC=ZBV25n(gWW1X6Gi+fY#$mj# zk@O=IVI0ZBdX|wCUlKJ8(<)A-Z_o!Vn41=B`xuSudV@(9YpE{-38$b5sDwJYN*EOmoIJlm z6GAWm%KprQAE;hpem5i{Sr^rW_-G3Uq0b44vmPAyDNN!EOt#)aJ9JFiHvThLL@8W9 zZ4(KM_F&5R0P{B6+h-nJoWnpK9l|`K*&BS8@kB@i9@sH2diw2X5{4Pt0ji!c^J2!F zx!|x*4^>^CqTM}0KtNP90~wU)mHOUsNawp3)kk0Zkokwk4;*=jKU9C|^!J6y1PJ8>iE-yLvY3lbW%>0(ayL^1ez3`UZv`%&FTbNA4CDWJh zx<&indH8$yW8K10U@K*Q!|ZlP>fZMm_jTSny=DCKGA+RL55;YF@cKqj{d%+Ur~k+4 z;_v;`u=k}q2=bi==?%m3`xv6X{^jcj9~`uP(LLg>UZ-qOKT5oR(Sk^cCGQRB_ZHAR zDt;y+r9!~hNHGitd28Z0B$vbx8?Pk<@r2lxkPoe$O}Z{$l1wJR`X$Oj4SFvkh}3Ob@EJD&FkhD_${8&&F8mL;%qoHSGY|R*!*d^z!3in-YIoX zn|YTuQm*9}A0wI(Qv^$XU6Uj+9<2qN>opSJCHDOKcSP#~@zz3GzQSzFL)kR7lKE&M zWp1H;G2FDKR?R*i%uSkX_Gz(twdM)b8>EEsh5{LR{*| zjhtVgA++fS(hIkbJ>1dIsvZQmfT_^Wp{aD8I$`B7eIXA-?3D8oCcwJEY-9zY(l;s) zb?%V%U*e#AFhjh_n~mvjP5l}=EU=c;R`p;KWilnWJ#5mc+1K2O z{1U|-{0m%7%PTZUXxAFsvzTEmIb%dS{x0)I9N?j)I%#j1P`v{VU$p&^)?+S&l-+Yj zdib2QkfvW>t6f3cK-(2~sOeK$-GK0up_)RNwh(8xVLG6yA%qz-=FN)Eolog=Bgj!U zQ-U>u4--s-nxYndEHnJHIys}GQ+;^;qVgz~C6S)mNWP8Arc__Ez6A`#3T;{c*7CUtY(?pIZWXM$MY5% z7Nb&0#7&8O)}gO^5bdU2p)qQ}oExQ!Xb6E_B74cXC=%e?4Tqgd5Tj8$eRv8J-NZ!g zX{AGauZ=kqJ|Z(17!HfCj^265IKp`l6k>A0%w=Avq0{VA;lqNtH{o>m1=>Ekyc#Ty zn3Lce&7M3r#eP2kPKfE;T! zH1oT@Vt%0!I=I)b?j1a$&(XNRkc{M^#zatKZqSLCNfIgS=mdraSix;QEUp{P)9U#6 zvN}6Ef_a!##{~8mq0Jp%>T`nc5$kzQ=;~*WKMc;<^bs6gp`DT8!6(W2t{`B3F|N*^ zex-Ujem7>1Z8Wr7+P9R+r@bqJ4~VbyJyJZB{RPdeCXY+h(6jRoU{b(I^o46r#N*o3 z98z;m@qs%|_7BpS&dc9Se%_R;eqJ;wE?3g=U;JLD%>(o9^Z4bS;=WPR?DO4vpL$c* ztB00de^Ocj=_Wy4*3AQNUV~dm3hbp7uch4osG+RCzfqb3@5jKq=_7LpOX&*fjJm9k z0eq8gSN+SZ#Y=GL`}E1D;gSUT&35}2|A)udzw)QvK796RfcY-L_=aKmeT>HZt=>PF zv=yb^&1-}t_{WI|#?~JK%LDEtu(JI3Yd;JLm?4LY@j}~R(UO}?A*6XI=75-rEiG>& zsU=NeKnej5hDo{*@YcY8k_6GKS*VF}s|~XvLXr^W5;@gyh)|0p$L~VCTTXc{663kQ zk<`{@CRA|pQ7IIrvGz+NA?zeXg_VuBfMmJWS<(nkT~FZPrHoL0mHJFoU=U#|0rd%c zSMmlRYO#DbD*MO2vYl>3>|cS)_x|Q}2sq1@VevbfmJqe}^S)QiA`(~~etS5s ztNBsF+=dWXY>2u#~7&j=@0?w=#(^2s&kkX9o_XIXo6zY)(w$(D|w$ol15L1-HZ%?g(gj7tb{qbAu$K*pIDq{A`!0s?o_#4L?; z>zMh-q5y;a!k@0@b}~&wTiZB~Fg5E)%))SWVWJ%4whk1GU7`a*lzJvkocf)EJh$7_ z0Bm@tUrw8HWqLFTnlq=iw6#ZgV6`@$d%=|@I8~sfYFA`uI_#9%T@2~PW9IPw_p$+)BkE8=?A3! zYFhMPVgg0exI>d;56EzKH2Z;BSP}ksOLVLmPVV97=>Y5GHF2vuUKcnOYDuKLa#NWBOrAh~jgI?W1Eu9y30S^#lj=)6)kWw#Yhh zeG){YgD;4TZy)omHNhS(VAu>hK4V^7H7FD2lSVNNXm`*=7^Zn>JY;5m4-=*XeL@UB z&KWBe5yQ@jHa5a1sWT)UW?rtp4$kTxV0m=NoMPTzs!`B!A2{cX%7%?F-hk`w2Oi^JC^a_=dT1%%Q2X=`ns~n6VNb`G_C|Tjq4*2u7WCHJhP1 zdHzwgBalGqUHKYJ176jN<+ljXX1xVI#E{*-;8Z>lx-*viI?c&S3mQ< zTt(ngHsQj)yh`(`?mG4F_FdX>t1iY8CD}=-T#S2M$?Y7y{lCMU;64g*ayak~oABicKZ9YgkguVpf9${PV4bkp1 zev{lxg~sPj+w+7@+6^*vdsM?&ASsa25>G_3_^LrMAjI`Ygvc9krht+DL=w{^p$ zSXO<7$=k!3K2CXMTuf$uKOJE1uxZ(O_IU$Bt6CuOL|XXlqpu+qHZl%`6VCB3zxJYf z_p2WiM_o*55E$q3Fu8+M?dfq+MuI94J%VVE7MS3>a=}JF!;Gu}(J&;64>%Q47(x-E z?+T*ifbgRS5HXYZoX`$7?Gf4;4`;M1+41A-E=#9WRR&-w6UnUyj*HxgjzNl(O~TtAjScL2R;p^3g!diiL5+> z@R0CS(u@g*M936^ROZCZZHFdBW<}Gmm<#f|fks9xi{p{u$sd>w%2&c~zbCC@oAp5} zZ4V7s3*%UO{)nM}0|P|;PO~c*78(8?sshbQTG;1zAsVq~g2BTt!w6YGqAh$6N&O6; zfhh#M%WaGQkWyPpAfKH`Q9Nit*f)@RCx8NQEg;xrJ{$Vmz@$x)xGx~0vlj3H+3I(M z{|7uGL>Nv0%-;oTl^$W7)M}xI&>z6Kp>1l!+SI8z-W+pR&o-H$m0%*m+@dXFyfW43lO>uJ+ok4V1R>7B zzKOo|;^kA!%s2=Xf_SKJiS5_qP{I({PVni_)Je_H42EaT_XbIS%yNX4e;!`g@la9Q zCU}(5H^j!o`YOo;Yjf5uy>ANvZ%mSuO?Nk?y>F9(gUW5c+d3Fs9WYfL`UXn65 zZLQtk#P{O?c)@@jn9sp%_h8z(Ja5w$(Tw4nJ)cQ4Nli_sq;+oRRC>l>i8f^V5^XH= ze#vhg(y59*z|CU{W()=b z#z<4j9sVSm7OIt5qowPCLwA_)U0zUVzUH>%6=ISeff2u!^Ph0d5K7cU+&cUd#^f##Jv*69Xj zoD~Cnjp{8lI$!+4=c_ONlRr{*VV<^V^c4^=x~vs(z8Am9|N2FHvz~ly+VZyEYia?W zdy{J@c#$-9y4z*dd2TMMCGrrHBxqoiCbvgC7X};NYQx+Ln&RnVCwd6Nx zUVHkjegt8^R$k4B)HyA;ewJ^6clwYZAO5rf>a(Dc|JVIP&^`qhXURPHcmKj)?ELc2 z{p_!P3c$THAS!-)bA6jV@K;|feh6me&wrcAKapgA1c~_x`((YKgY^oQ0UlR#A?ky9 zSo|TVNn9dP2{H&Il%*b*GSp&L%f@XOQ&F~Q5sZV$+iEf*^Gpp1dJDZ zRW40O+^oo$Is>`z1U8^4Am^R9s)kqi{3pEDor}#T&W#c;?4Gts21~Oy3C`Hi`)m(` zb@+)iZ+~w&z8CfyS%$#@K3Z77^UE^)Pt|UQ_Do==T{rniqp(;w*@v>Z`L2HNCKAwj zb`y#jVp_u8G||2Y5BFsE4q_Xk5dx_P5x+qas&wDbzcWs|mnfKWW`UA$qYFvI#}tdc z=-`m8`H_-&NoTu-kYnP}gs?t3JwZ|r z9Q+$$$_0NDlcXI|ZB5h+&1>|d(TPsnXlEcCb=KFpz6rq#MTy1+(+mhz38|5Xzd`sZ zZ5O5u(ro*-i8e@we;xdh??a5pm_k4jZe@!Pj{dl6OC}SvM3j%E8H6JQ5JZvA-p&K1 zw;c#EO~sl_qy`_O3fY^EW;9TK^UDZAmcP|>iQgJ|Q4AlP#rem8(Eko5AIHP_0FjbN zWx(rnpe6JL=UAYDswJ>p&o==U8N4n8m8N+T`i`kKDjhjS(#yIm^_79SAQA1Se~|Rg zq1ZL$lRxwP8vUR&@c~rE7bqFyj@l{b9qULxm=)!%m5K&Z2t z1MgDDoAe9gAZZKHs$Ub`%r{Q$Fc3h$O@j%qUBTP7P#-AaSfJG!?*+F$52Iso}6sA>Xicr*OU_67!C}jhj_Q_$dk#kQ5p#RNIX}-~0u6dFnT_$~!4F8)ko^Jkr~SI^L4(Y~Gm0l;6g*%gc*tF*&7S43*VFHaoY zMJUh%f51&~meUKG(7{(_jb`S6gF%n0=U+Xqe)IqPhv1ZtC`%lnzYzV25=usO15?*R zE2Zz5+CZ5W1rhpYDKuI#Pgmfc`S=CQ<|Ed*QUng5W%0SU81mdki$D02_Vagt!(0ds z**_6saQUq4nf^9!!6kXQ|C9YFc`fw!cGY!ezas0avdaB8E3Hm%{`&r#rS*qk=l6Zy0poq<;lIdy`d?w_KP}g9HJg9_Z!H%8{h#Ue ze(lo&^Cu1G8;0fE9D$#~V|y~&{0cbLJuM68+syhMOZElAXui!y;=D!}I_kDSrbWdr zH$g&Zj{D5_+~yB1CKeGFI{<@vM?w$-G!X0=%FR$q~|)2Pv;cG_N5$2@cAs z`>syo1JmhiaKoKgJ?!n^T?NGh84&$iGh`aVFecGB=vM`P=e+9LHqJ zr}YpYNp|zcT&DG#B>BE!8hEZFc(fH1B~X5>mHNZ}0>V4M=3~ap)lt znPy1wdn4c_YLhI_h!dqf_|46HMZfDXEpfKMVYkO-z9q6)7fJ6mbP z7Z8nYB%9Dv5Wo-z5MlQ39HvL-U?s5z8tCFf(4-9ep~oECa6sG=GZU%_an-_aVxiQQ z{DwOONWo*e1>-0|xx$plfD&qQ)WTeG z+Pb5rMprGTb5|nPu`&|X0_|9WOh5!`KGwkOMy=Zhrfk@U0LPr?atxCVyq?wLnJ6A^ zs~K6CMI)GrQxc7)CRVC9%5K&DoFbh-Hoxcj|~&uMGNrg zdp<|F?=MpaLIBQ}(d}PM%D@7I?L{*+zxqTJ z+{LV|&zi-bYzw1p7;nyUuP#w9eKdPnJ%BJjZgQX^jLsD%nq3^fhe!+V9iCMaCR>}2 zCZWz59sBhcYU5X;U&z%4O$^!u{gH;u=_v%anmQSyZPuWEWiXrIkHh051Q+TtffI(M zjcMXN7+B4*rmW#E+L#GittQM=6RlALd~wY>ZyWT0b#46zn5K;<`mg~HD&EOqn0fFEu`^1x%Y!#)!1GL<({5>|S+*_U(|UaciTm!H9v*s@0WReA;~U z5C#Wf4Q0ucq8~%!VQ;lK$LC$l)E;9__ZhSD?YDfC0Wv&oeANyM!?48`v+=AqAgz?@_@@I^0{%=$Fzw$G?-J_o|{N9`ETlT;khUHs!QAscy zufO!P-+bV&k59aBvxtAJ$?hXr`XMBPK?-iZtoFc}g$()I0a1Y`azTGB}*-qg7-E2Rlb0X0Qi zS0O0#Gk;`a{M2;@9!hr4u*LdSD5a8Mqi8;So0E-N0@QkJmxO77G{pv1%gl2M(_uMG z9)*-mNMgvV0H{iPS=GUAj z(1GZkA|2fzJsu#LIwb9ms9JM%NMcdb8fK;X5KqY{!%#Ved)o zMhxoVz^nbg^I?m#c!R_K)bK2^pI=TO9?%ADAs{(sks|3s{%IzpW+6-_b5;Vp#WOom zO;^rgNuQRDmyV||SPw93j+drB)W~C*4#x)~#*TAbN#hVpG#fR~h)SNjQu|bCGUE){ z;#1Rw5bNkofLHv@#7~+!!-p zA0P(aLfmRrrRYE+JfWq5C(NFj31Hp19TDFs$~SnyfCwpb_&lHIr%4 zf5W~;R|Da!Nm9Wb_U95UfaaT157)ztt4TX$mfEyYYdK~q?WHaWaJ7DnizkVz;tbLeK z!^m!d#l!yAOcELW8FR3QW}(k-cvbp?vT0)zhHnA!{OsZ>V{!oDZu?k2^zT8B^HV72 z+BN4$;IpNVQ+oa`5MyJMuh0ZfT7_hDk@L>ZgHNXTrBI16M<*l!uJv(vu9ed?1C zhjF0~PESt}643v1G#H#^Gq^vD@H1AWGVbFsGQSm&d$$%NppAv?dFzA_pbm$2V?9;0QG)?2oeGj0W zA&hW7K7E0&$OugcK?24wX-mu-k!J*V*k6;)OW+_N0gPDsi_qH_L>60uhkAr!@AuFg z1N-qA{&SSMVC^+vR++Gz-vR>$4lqhv7mb%QXR^d!85k#*Q^sgsz5UK-31iMm0N=kv zkn`;E2i1ck=GqDP=<;c`dUjPk{qVP$R~%Z47Ra-dWa4#?YtoLE=6x`9(Y&$dq74jF z$+`d+8ws&H8C37F$F4fWN-I;FwdXpMO`|S_dCWI)R>A+?)C>&efIxViG`XRW`kCp2 z?=6^7ClQ}l*T?sj@`f}ikYDvTw-)hg7xtAq)z@E^S>QC!t^2gbHS+pZ!%%;lx4tha z%_02zOPJE00>kTROPzJn+wbZU{El||>*^`F@?I#xEmk8s{|S7$o`MrTa4hc7_3^%} zL3@g6*cX?ZFY)_N)z991fAb!A!?1kwzPWkxpS_&_(|62p1C0RqU#k((-aei$Gx>d?u2pQA|l%2)CF|B`ri83qNTnB4{sp3UV!l@^_Dz zti^J?_Ft*P-$9zD%YV^(QWwIIdxA37uET* zi|WEqub4Hc1@P4O&6;xs+MXAobRVmE!rV#`O2BP3+o6s*Qg;u7>miwHL!irW36kGAH4W1Cdw_Iv3Gq6g(jJ_Z zw@CNZtVm>QN~G#%L;aqvErGk{ph6FCR9b4eI=9O#ICZy#HdaHX6YmB%=9v7fklZ#f zS203Q7(Kq$Fgg0F#BrNGmGG6Ja85=`!MFVvg2K7RI|&BY4JrWYMGHcjHUyiQpLrUPlrvOGoQ!wq_JxfJ@3{T3&fCR}qGJdH4k;FEB0G7A)3=xdJIz%gKqp|S( z6K%X?AY{@85OSVSzNDR7`VuyR*H%uOkan+RQZN}Bad2IS017&ZYyPDoO!h@*W}Uzj z36H}!`^2?X)1b-K5ED5-j`X{8ARz>C4BYzO$l)yLuN4K!)Hrsw$(g3Jf18flq3v2F zxB+LRAUS8|KqsP&(Uc8NN;9kt{jQmmjGT;=^SLYG%sf(ivBq~sl+)+n!w$r^TBa2Q zzl^F~y_U&Bld;8TL0^j|42tKEG$4{Y2x9a-lJU^otxdW%>GV%P??|6=E{=UcwQHQ) z2WTn|V47E$HNrk;9mwc=oX*NKcj#w9Z!!0l^v@jP+cP_cgCiJ#mpSBcig17tq3vpy z5ZoY~j{(0%!TN<1^+X{t!6QZ&#!0^lc)6;gce>*8`66bIcfFUOxBv zx2x-Clj09{f`*U~yk{^go?SA88M=Zwxp>O`5MK?11xEi;lhOdjfxbo%?At4R$piuwn3`?S-uA(9jm7ioieMx?7?C4_ z9n4>H*3Shv6Mvrt8kNcP0{lu@`k0}yImUbsR+@e*YojKBJ=~QButw>B7?Uper@cO? z-X%2igVj6CN9Ls=+IX8l8^JC7*<2f@0Z+{3{^6bz;TP%LGra0_`LX{~ z-hqQiR{(K9j=%EO>3JYRu(d*?c8SytSOy8H+9w}?)#+9=g*Qx_-s?@L;KN73y6OS-_2hd zHmeDH#;4)>O0)4(zc88q2kkF~*!>o1KZ)1k zsvjdFSWl7S4Z#lrWjVXBeJ=s;Mk8V3CBsrn>+5#;9-1td>BV+6;V**mBS9mw^tp(u ze(rriWCelL#Cr%&>Nj1=ByY(h0x#RQpTNO0^SJ?tgGBTa{9#~% zl&rU;FX@0y28azwhGPSsnO(bh-+m`?W-g8-&eP#tWVT*ZQw<}R&l99f!EbeWC zs(kbM?LRyGro9wrZB)r)zWqlZS`3d;zt57*KBRYrR|&t8B}1TQ)1$u)gX^XkM`<9H zz@DK^P&95dsTC2pE;;djPBfz?n`i@~PIDl`5^EONlNfj0AiR`{E7@1b9c=*wuM*k? zgmf3D)h$E}`**YhLl~0@1Q;SyZ2RBGjEvL9A=ovSAWT>0m;MzoFYh5bbCalqE!~6g+*V0}cbE@0D5-=aO0&^EftiBxOmkfd2N+bKoJ}x`F6( zJen{@vdoDh!~?VG7uCk$y*Y!RLUUwvEPYlWV<^YyToTl6h)&LifG}&r=;(O9hB;eP zH!~h$5v>sQ8g8}?A#A8<&2w6t5eyyk96}FT5}1J--vo`_wn*=HW5yEY*anjzDbW^$ zzeKSnk(qC_vrRqr`w+%J=EZyad86r%T}1RT*Ovrmi)Z%N4q{p#8MO-umrcGkZSb`9 zE#u?4J%&==_8>4FE5^d`!3`wwjCy=-GzX%4u<3_cB%hauiqZk#9IMnhAF~?f_73K! z4e<&6&0JI)Lx1pFhEM+xnLq5CD0{}-!FinKI#!++)6s7P=W3aVEd*}^g0TZ*vWBTq z8n4-(Og_Cp|B25W%c5P80q)`hu>wy{@rzk8rCX=~7BFO8=JGuV*9Fn>;1(d<(NM^E z;oqR2PzxfzD_1K~#Msz0O)IZoJg>g@^WOurLL@A-H!o<*@xk|#w^LnUGWY28ej-!R zc!=}^1Pg7Bh#}q-J}fPwNU(z8vM{Tp{4s|ZPA&*Dygj77$Eo+4wXEa5nv4N+WJU<# zOZxB&Kk$PD&_EzSKeQRY7VCD&TJ&rlbyoWR5Ub2-46v94Qg4I#$3; z8l6_P2@=p`t{Pauzy@11OX8t9>(x-(8<-)Pz3~-?b7B(JCm_OG@8Ht{G5>b|Ey^ag zGz(;=uMnnqj!y@n1|yn}p@}D$o2hk~FsD72$uQ7rXA-D|Ii+@JLC0RfRM05!%3-3Q z^(%-j;a?Qp~aazgJFa@nhdK+ z7r#3CVM!1UHNXP}DBvoC3b-y{^@&h+03+0^#^OP>*0jf6lNE**+6aj-Ev!RMiK$+| zNY3f2K66boSEJ2oo+Zaj;mW%@ zl__7pZtB?ghi~0OlVjfxzA{~X-OiJDX@A*k3nYSS`XY1^7U6c^98mV3K%4(}Fe$X> z@A@T-Pr98iL2Ewx`Li#^ke3nGyb3M)4OA zcH1BkXTBPtS^*siApR>7G~d~N`?Vl>4GDP7Gl(b4H?8o~2ikZGpC{PcCmB=Q!p9Mi zD6t%91PD(|F&ux0W_=+%e+8Lk+2rU4$bBk+q2S*V`};M|nxtJLiE?I-1}Bjy8}&v- zP74#576e}dLf{a>d#c?w2d*vfmm$V0b)ZdINW>LsxwMe;s)w!n97pk0>c>tUB6|YS zXOMs`RJbM+hCsH#9SB&7wq&HMB=(K=wZfduQ?%7+sOdBOvk|u9Fb~l&A^hvcxyhi~ z2cD6kS(6fU$KB!JC25?W`?!W*IGmng=5|bvJ4Ta3V*05>u;;q2XI$I$kBp6sh$dBP1EJJ-4&e(dOhX7o#|*|oEfxLLg;`KD>_LfJ-tGAA z%7jBas|^sRv|&~nyCG8tPMUUT9<~VYg zMV|Ml17938AlEqeugR~Y{0qVk_Xl7WGEOfp;GJc-VZ<K~^kE;r z5)T3$z(h7+c4jaw8-N)M`_;iQ+A@4g2CD%+BF*agv**lRG**oF2K>JjsNbWd$J zbBnxvnVu6g#$D##c3d68h^RpxoN^d0p}H@hoLBSn%W4H~TL8xj&Ubh`tR8GWi;Rbn z-w|0+K?3EhJmZJ9jhIKYlV}GSs@Ar=^#bcf7f~Hr#IEqu-`l|KOZVJPwKlV z%jd7^FTcd!;$yz^()WG}bTJX#zqibKFLkeWpMIM;^v7V1{|^r@{S;hqDT6N_9{+tn z{qau$xStq^`|a=(tN%_{{8uiQKmD}Z{IRZRyjH*+f5Qy{> zT6CYts9~Brkg(Lde=d@fv<3JA{~n&2QHVB*w70j8Fg2vJUMrP|u+2Uf4)fTy0i&C8Cl9 zB-$mV?c~#B$?Z%Zj5sPlY`4_5P`6AW7C}`X6Ap<;L**J3rwLQA9l+QCRw#dnpE<-b zZnT9s#SF}Ltz;yKEad#_NQ`M`4#BSkw5Q!Yj<-6yu7m@kOad{TMBemO*@__4Aod{E zC5#s^Dho_<+Kh!ntcUV-=!b;6MFXM4*twv_OGk8>K*xMTK5zWm!SJ}bN-37HD~#R- zI13EUF4qS{F8{pLL>V~3vB;P(7BUB(o8q|iW8#GG1?!^&;WFU2Qfu6?A@r2W6Cn&| zdZ2%2m%>`e+`j5XUxWEk2DJl=r9Y` z@2XtG=)jtVv9SCtp@k*tdT0Z>nDN2V@-MAmL1|{E6dfFZKYewV@vt5^MZp)aiQaRV&0> zqi<=p*MZ2F@V2*&T&G`v^HPo90ETr3@oPl0C3Qe6(yxr0+9SA{AsQ@1_Lv-EYK1m{ z6p>;Z)y~lC8;D=crZy6vF^QvpG3{wXM9**z_wd3t3{-=%nw~|%xI%Jg-pOF+JR=yG z!^UZtnm+zDONjOfW|oiMdJAWD%Hhz-Cd9Cg-UdwAVREDpUH_#c7}O49^y1?EF#QGq zSioGJa}eaMC?1! zD{Br0T=oS#DZX5}*Wf$!2lze`ClGd=>A<|_8wS`2e0!8b`4qsK$sWqRI|C>rd#C<# zxkw`JaZm8Foci)fA?lMzaFec1Yem@yQ+8gPB=w~3lx%7Hbkt$`*RK5B*Y7+3-=rxp z`>=jiGYWMX_4E37{e4qJX#|hC*;kz}a6@pek9}~H!4gUznX=D5BG?N1zk*=M5aosSnwtQ zDNEQ|Owk;W7sM(NZ;_F3STa8fF;00TR=O(#<41ef|)cz~|-t z&3n=}KrnT_x-P$kCef>?!FGo5Cx4B^!yE{U@7VZ+LEx>^t1uYkB~9M%p9N;pgis}) z@BP%}T88)LF|Ck@{`>{4!MjqvOpi=kU?sgz9fREL^Z6t2SSIDU2kOvl8TPyW7MdhU zu=RAZrYkK+B6X(}Tayk5MeX0`m}X(GgKW2j7K4N3o)XH_Q{o|jE6k@fiA$8Rj5EXp z>GS~*cnOsjyK)yq-Xx^a4x&Wr#1rR7NSn8YK}FpbGJBLW!5n14nHa*qm|*?^JhsPBy_uU3D$WSHs%+qJOGet#PM+xu;Yr=J(L)H0 z%;Z6!YL3R#+tCk1W=%=F{mHH4pxK)m4n~z<&Jo_Kz3{D6g2Xh$4HJg6C5@Yz{hn~z zq?g?9LvVPCyP64^1ep;DRT(-Xut_A#Lb#~ZB#n^FYC@waOM)m+4moHurX~lu#?GzJDXL=bb zhMzRFiFV06sa;#5Rf*XjP4@g6+ohCS$7~veHU!@j##kD-y<}_{fCc@ge}R5NGMa79 zh-q?$hh{oz**r~Mp@w~-iC@gDWOC?JimzsrF@va6gMzRbf)C~ZhQz}bJr_h~p~rmd z!T@`irXN|Mm^o5sA12TF+1CCISh7}hDPYXo^repHZA916!Z?n!!*e@EFx4TVjV6b) zwNh>5`4G`)Kn6HZLjbo}rk z{j;f_PCg3ZuBODkGAM!0`yTn(W}d9q9GJP>8N$eS>HkXte{_frr%#psO)b{P2va%3md`N_JRKTk zjXCI=r~j7*=wR(Pi2P^x^>@GemCVi0e(yhl>6=n>w1&((O<6VdyT+$w!aTbF@BzUD zh6D$hRbQQamFQ$dvm*-G$zi{`caQT}I3H&;I-!3E>3mI;MEsEE2n>3(p~1I&k(QHe z0WjUH8TwYekS2*>O`^7~t1q%D8sH<(GcrVZusv{#Q`G*7d*w^AuaRHeLm~?GPq{3& z-iO=+X^B&uXZ!({dT(|wvWV9&ccD_O&+EMkY(O3R#O_NL$YouoslW4Z|Ho$~$E&mg z>ru+_~eAa5xt3AHJr0 zlh&FF6UCk_VxwO1xm_m#nefn6 z4HDMWBIH>RlhPm0t9W%4#3ce14;G1$`F#!&v3%}Dyg?TH4uai3D@d*&M9npXd4 zXZThL=su<^1J2jbv_W4FO@$zj*@Pi-AsDY=TsBCdC1`eR=1POtNRpe3hyE%%Ou8B{ z8EPan@mU(lj167;{uNr2HF;Osrn70t%*8)}F_v-C7snIOHRtI=xM-%fh5%K1t)yNi ziHh)F;Y)&&y2E!!yek5wk9M>#{pd1GU9Qbh-dM z{dN0*=r5t;KRz08f%f+nMs-cQHq6&Uv>Pqbu349p z^DkEqI-kcMj5PeSjh-?ets--024gfrbJ3%nhO%D3B)2`sL?0mfpd*9FcrB-w^oHX| zr}brRav z^#mAzdB|AzVVHX`hb(r^7a|?EhJHRotFppuRNT;nvFXsKu4jWd^w37S3a<%FadibV zGCR+naZJ5*E^sk#sduwQCqln5>huv>OX?b)yj7h(xL-Yc@t6o`YS}0o|CbRF}+ z&xf>0ydE=b)}`9V9Fl5lvL?!`qcFz5%s=O6_AYRZYXi)Y{Qwz-$$Yy{>;3Cjov+^e zUVEFoJ}G-okR8A3wB_4grg(z79?8`EJ-j+Xg$l~m&rb)(vbIU$Tjg*J{gEESi*5U!70KgC2 zj;enJf!Hsh{a)xL%v0~v86p$PIIP4a|-Du_cOT0V%P zb15Yf_0^^MErU{!5kIVuOJ>AWH6p5+0-234F(#_h0%w6+W`ukq89tVfWckBAKv>=u zLP80$a7f74{v(#1_hR0XH5;sD7v#EsNO|7LaD|xNx7%ldmpZbKSg#itPf*nd6!SFm zlUi7O!6kA|J?7;T$)JS%zT-;Grd3*I8-d9@d?)h zdq{E5GcaNg6jb_4!at`qW0qxfEYC2}lw$4K6*!LS*sb|epADNI5SB;@l^Q}=9Vt;~ zgV*7EaQ|)UgCNnT1Q_&vpb8||FdT&cG+b>5!e=KTgqmGO0R2FurAED4;2fEzWG$st7S{kZ_ z=<7fLHi)ifn>^zrj2Cl3ViDCy)l)OU_!^Zf2MF#7eQ3oZVWuBwBJ+;9xF1L7@Paw7 zNfBK~-!N0nhZC`WJsrj?r^v&Es8rG)$$3Wa(oP95n@R*Rq|R!DD&|O!wE7(JnsyR8 zmoXNG1Ph>C*Mi)hem9C6Jbz^b$F`E1FpsxVAes@#Krx3Mcapnh+hH z*JuFLMy)BN4jaCO8PQ*1i^&c{590;=%#lMhWIA}45a=>j)B|BU8XfYUnLXz$4z)^B zkMyetb=sEQ9BmC_)#KTc2zovITp0H1iWB48m>cz&FB|+JwlJmRCs#O%qeY@^ng<%@ zc#N>2OUN;02Kb7MIOK6~3G)DBVH7RRk7k%dt?YB=w^7N|YW0AnN1h=R z|C9q5ahpjH0LPXg34UDDh8^>>4U?ddqJd_GsS3lVqdw2TF^8P31fU#7iSs*nCHNY1 zM711h{}zP)9ipu}9<{0q*0REd>EaSIH=Olp&y2tm*Dq-okbH zGpTyVo$A3Ov@FMH88Pi!v0fJ7x+l*cqgBG}j)L<&Aj(FXg(1d}o=$ zWE;I~k9YZ*B>uhyTi%tl*?0H9YgkIDJhXoD`8Ui8kHiL6E5oOT*Daqxr!L;`+;>I` zG?z3B!;^P1z4u7)Ct&9PXBgDGc-ijKD+Rju8`U59TZ`3S`ZK-Ge{vV${Ul*xWxcs> z_rPDhoPY7W-}#@m(RJQ_b2sk4|8iMAHpj?THMEM~kz_vjwskhWpBSsUap486|(ny|oiM(FbnMWbeM082R z`w;X=ONq9Ja^$3Wwg2NyLB7!vxiffKI9K4>x2r~;c|i#`zeyT1xwOqTQ=VOx=Owjr zu9&6(nH6j1CT!MO!|0n#4f1*KmHNH0OwyY#FM&w{Mh_LAF2!(^dlw z*yarzzvd!2*`0bj5ZWywE_KoVXev{7OkfAB4uoGD21?Tzi86(qIlNSj3YrC(IipXt zA@XIw+7P@F+B!FP&}tl_)j2xi+zaY78kSlpO>op~w2%;M%HrXQIrPvb;9JleVbBGU zDpM(^(7~azL%+yK>8#(yRzDgJ&X#DoP=9p+F&+_)Ffd-A#Ac}FX+Ps z^I*q7@35s`BbA3s3)7?4ju+5Gv?1njWqrhdhso>a+=i=b)9%bua;a@O^85@a!VZ2e zBcmljJi3dkaTyYF`QgJ;#vC6S#)@uA`SHVo09Yd7_JBo7&peg^pqdb*I$SGxSNLIw z>C$%cpeb?w?x?ecRs$NAaguppoS9#m6B*b*4POomWNv6`=AiZ=k`G4*B_fyuiEo7F zYzY~w#*jICbU=I19z92k$1JCMsYuh379wR4Yl0*`ZizC=R=#bljf zwt1!>5fhyG!TjX>Aj1z|!x)%G2mT4f^(Gn|B4A;nhKvR<#+beVeiK1vj(;q(aY9^`nzV$OF1^^_wm(L z^%9e|)fn?RVCfI>2|*JPKNaAO&l&UBPEmWLBfotyCMdx5rP?2a0ca`=!r*-AGH+cI zW3*Axq@jg#EQg#0wPlPf?+V;^frhIgE}_l|;hlT ztD{E`!zhaTqp>DU=0D#Gqokl6ubaJrxsyG__q^LDAz_l{wmPf{gBM3K?%-_Q~&d`PL*gNt1hi3$p@2zzvL%#!yEMr`0%*{kAF27r*VN zGcqZFxohXM*bjj9br*7opZK%?!GHAM{_=nNa~N8_x#}JOJ-)ea_rSc>{M98Cr;Fwr zO{Cjtzti^*knsH>91C5AS@bSO?nNFYsuxrR&xD=jns?sS2qMUng)0JaF-P-3`k0dN zTV!CG1w@0JBKv*_>pH)6@d#{DY6x-9vy@BT(qC@t0WruzqFeZatWCTBXJR+vXi0dM zG(1c@_-22l5Q+aioIz9)9Kaw(q7v(|?pI7nk%rnXO&RJzR1+15#k>|v8P-#WV67ec z?Yz>~g6M?Viz)=Sl)<+ftz~F-W4U$xVM0h)|5o~FzVtJN*v3+b{RlD0<^*F5L00-* zWGmcWf~tRkj9rKc@=AOgJxNLama;bx7@lP^=49>_XGm!3qnVHWCVAe2SxHAKF{I3v zl6T5KfS73VPCN1tv2I=x$PhT5gd$Oq2wD(H2{($wc?|`+=0LhB1ce3|5*Q5#Bsaqu zPX7x++fLc=jXtI$wPrE;MalSN>>V>&?WyYn9Ele&BKo^P31T+H8yE$psZ9vHCU*TT zh`A=js)S|wne1B#V53*5gKCl9=vZ6Yqm(n77=&j`gCO9$Ff;>*o=7FhIm-lO=^is7e$I5x6i5SFHhp@DUP}M)iQ?Q4JnADKYkiQ)mm9spTu1Ss?nX7^0b30z>1N(VwPw zo|0bEF$sOAXA|aXgIS}YVO?Aa2*CVtKKt9lKTW%a!mykjZM}Xr&-_#?au0K+HD*bg z>ewsxiB9U*_?k4BS2;|PKI-D*VaVVXnyAT~s8Eb^pK+I6)D%n;o)OxhIp&lrv|ukj z9AhFW!D~2leLlcXsLb@4@O&8=qb+G6jg#k)IT+t2h~Xs~rmm;m_YYxEUoy8bIci)& z{35X@1o0dbtqn}?h6qzj`e(qICx-OaJZwBYXMBL=n7NOpW%J^iHnUb61WiD@Fh*mc zfMPIdR;Q0n=_55B_8W7PxzvRrLe+(q2AEl^bWs?F5Q^ZG1`PNT#%BvrZ!nA<45{a? zc%Y_cV0{i_TylSYNgv^dv!q{TkjD`BAD~I`+?{LY3bT)Yj0c(_{eD)&hW4D4sf;3w zX@Uu$Y1PM}iEqyUElE?8K}^^ZID_?RAcl4q9Ku{Tir)-o%flQunCvWj_|9?IsHSrr zIbQm;M-(?$LJsz%pTG$hFsvgE!knS`G!h)%d$hqrDq$|@i|dQW^cjpHCYB@ocDl54 zi`m$W@s;`f@X6!q^PkfPjc9n7$JuwxW)Ao~%Gpy7(ah``L^0#p`7_`FN9-7T_X`g| z-ED`}Z$E!tJ))0gVmvj;f6>M;eHBVbq|20$nz3?9(`S5>nUt@7J8$c~NiClXT@%D? zT&6cOxNBcb@PJSAVE@;0EmIgG;Nsqp-q{x^-!qI-&wkGqNBGbGej*%WE?I&}tS(8( z7lNNM!rbsMbVh0;xj7i;;MdYPpX9mNq~7oIfI`a~s!ZBEC9iOnI(=dq*RyNiwz!`L zuaPKGBlj=}PcHCvdX#eLx)kJST22|2Qmbw9$v+3a(h2`v37b1~mEM0Hpc$R}vfuty zo__B2H{adASaxr&x(EJ?m-GMfqhb4x`@2l_`pqZ*Jz%r^r#~{92}{@o%q(|dXTfLD z7veaui>UZ`)x@%kRs_SJ5dA?GymzsLXrdrBDn2jiNNOFXuo8PDW%#~4vI=PT{4UQ5 zf~g613HySeM1GoMr2xtjE)a{7b?dYA6kxg3d9@v-eB5Djkor zG?lvnOSHK~=ojKt@_@Tzq_YhVr-UGw5n;&?x=c$PS=AZ<2KBlZM-za=RZT|>rWkvP zPhCFTA)yPx5M<-VEW~-PX3Db-w%FjW+W5CXT-dKN6i_6xXX%>`jMRp9Zh0OJ2TZ{L zLfkVXuuCBo{YP0%d?uO@FphPD{5r_0sgR&+!3;UyWezPzVy>6~sL4K*XG-aL0Erf;^J=Ss$n?l(s}W|t2K zv;}7ZiN1#Bf%b|02ZJ@h?9DQiI(q=2OttNgZZP5(TzzX^%@Phhah|vPrW{K}Eej^?UV)$#%hH0Uxi@6QVmqe0leWkNH^U*m` zobokIQu}};9C(CZvp7xIB$(AU^q6Z$r^gw$js9NvCaCoppc!)>IX;nS(;xOLwg|lQha{IX_{LU|3w+`rmNFbkUE8-;}41 zg>%XAP|~k(LT#mfBLgCH^;i=d9Nil*5o>(FmYA?9sb0Vg87A3&RU0?Kq|PXFZA{x* zXiwLeV4@IYE;!d&V`$>SD4}AI@Yj^iAyZr6cuk|dGMEG73(q-6D|?Oe{G9dMY}(I; za)#kTo9{V2nncdfdMsh?=SDC?NFt*jGeqWzME8<0_M;re(8ulmme9>Zf?0qcz%Od3 z^uG~C&j8vGWowD)+!ex>$@nS^4;UflUhY|%DY*~1Coz!#UV@ohDnPuDb)r_&W-ZW$ zI4@v_s`KaPXl*z&vaeO4GPTq6V{pT(HQuJTzeDD=6%ix$Y| zJaLU!1K;*(9+57Wz;c-{=`4>#B_FwXoxO|KF?F*f9=`VRUHvTOSlZX8OT7W7;C1sP zrO)_R*J-+P1?u#Ta8OHG$KYt^in+x@nwiV27s0IWgw<`MQGFk6`E_u^UGis1Q}!*q z{dL)B9RKuR{0sO0&d>hr-@E<%yZPRA`{tqte&Ol(N53*`|9f|}GycePBbi80Kv3;h zuoK-yF4hTy!hJ#;XMu}EUbV(_60bE@^LM18ZVt6H10t)Kfw>74Lct=;7evC}n>s=4 z8l1x+GL2Vr!&Ymsz9f)JkUyI1UTBp1T;o(MWyZgZCL2*l* zmJJaK2?cqD?2*lSjFfVWiO8aXly}HmgHQpqBV@#`CYy8@2m29H^%X>;C)uAEiuVZO zitwflh^j;U2_mF~=!oq+M3M(d&Y);k5K;OSD9LQ<$PMwTKZP5^;u;v9L*$@&F=X#j zf*W!`Q?CU?cbFyTE$x+f+5nftkJ^bNB;`f{TOv`1>w%Djp;$n)!JKS8m=R*AC1Hk- z4m3v0ii|nXfH_d&)`Ad{7;|INrXPBc{*!nqmi)BT_V{CNNNz)5TJ8x`He1gq)A%F1}28~X;w3bFmlCA zad7BEoE{)mHYmXs2BAaW8(C zx&bpbl(9Pi7BmG@oyjRszoK3blOTxf1{$LtV>H2!K|<^R^S8wgf)|(qK5&c)24Up8 zEqz|Jx4^`Fc5U|Qr$qZAwLv3VgwfSsOQ-f3bBUgCp!v}Mw8f7as4c{9k94lv`4%mZ z{wZi+MlcF=N)kGxAPD-KwE!{ShhQD{kE<)j%&1~&x0D!;5VbH$$)8!=mECwL>rht&1TO(dd8Z+ zLR$#lf$=;gO!N{#kP#3M35alluglA8$6w~3v3BoL`?b8s*vqrK?;@mdUD~hoefC(| zTFhadyv`qsP8M!Jb)X;0a35jckakd`C7!bt?zib53b0uu z0Y1{EB&h|{@f)DkpZ+9s@RwjpZ&jC+ck$1!H(Eda%gg0|^{;ii|Ib~7?>i0C8-``y zN7m=*TCt)ZUgQ8DRc$%FyK51H--^RPXl--*tbidY1!1IuNORXn=r(!p>uPxJ8&B% zss)TI+N(5n#_t2#D8q()8HSt#*&y@U5J4S?EF+t(rw~mLc00^^gi#HVegke^hI{n{ zbNyk|rpTOa2+u`|egFE-xxv^$eA)MI%3+)!kaXTx`c0)_7Ij*e!Sy8gHgL{f;@5yC zssVA^fY97gqB&Y$|Bv>Z!PcUGKKjt@~2 zLU760%{iZgmeDuG1kicCz{JU!R-*Pv08t-(ucTS#Q`jXwJckEpEEYmiGcunsufPu- z%wTjLcSHBE#Ubqg5Qt4kXZos57^LFo;V=uE5_dEMn3W|s0hQ3J5TTfdL3A?0RD;1n0cU?)CKU&VCJAygAcYcRZR$K7-xMNRxmnSf&?^hJlC-t8^7QMrRhM~-pB`=L5j0||-xelH_h497_JebketMB}NeJT$k<=0vn1j>L zKfN|mE6-kFqQ-*(4WHf4I^SG?2V>kn3JJL5-?1y z4ysE7C~yy|;(Lo@`91m`(@%Ql**WcoK{J%JzANq!3O`o(koevw3)-U=Y&+o$CA2W& z9{t*`#(jrx?y?J z$N1e`(VxI#Sm_>85~qrAPYp_ds&xpLCa(5eo&mT@jk204eXAIK`Ve6XjG!hCCv^oDaY-Ogn$LkF{u%QBU#x|@et{Vp z@9&y_7dYR!>#%+&;CsWc(93`QV)2*X?YBQumd1CY+rRxfP9UnjpA(m3(jtMw!gn+F z3Q;WL;-^n)B3UHhLRJE9l%8lCK$36`xDwSc9ou z_$5`z>fazJ-?71Y=wXN~nF5}fKZpt>lsXjsauqyM*IOR=EJ%?&`6nWxOeM{!)88hg z2+dz4@;4AF!X!d;LlI$l*%wNYY3M#PEUn#rfr8q9&c7rk>{T>`D z(zYL|Hz`U&wjK#&>f&KOHe`QqJA$TpX8VQDO(h##3Pqa8^DSc|F=_;^4sEOJkZI90 zWCu~x20?H6UO;Hrfh+tJrbH;(K^%K-$r%5M6^9n;6y0S*Kfq*UP;t;2PNySGn&Pz0 zjyxd3&Imsi{YjS8DHE_{6Q5!Jafl?k(WDMEFWRopC{I%`H6fmlqECm>z}lK;QIBW7 z3<#+@=Nt=9vZf(0EoI};PePmd7SiVq(Z80Si2y^`Lux9c)@WYhkPPz#fr?dDwYnq< z8XIXFX{o*oj)4+yY%?Hk?PcbN9m{k5d7@!Js;kqlrXC&o%6=ez6=TP|fE0t23(*XD zhPKDGBaARq5HLHW+0tQ#hwei>!BS+-XtbsYl!RSHyPBA>G-PamT}_K7TMS?ZTLq4RT=xh$@dOIO63D<+jSEIDKkP*en zn>yNUB4*JJeQqROyJ(KuoYKDJoFe^yjFP4aVh09v(wI{=&*_UE5`X(`1JS)=J&l=I z1NzyupjnyPi4Nx+HEFNs>s%2Ac!-u{#JNT?yUqvCc=6Cj84)O9)&YHn8K`430FD{K zFqWL@(`Rlj^;L2C!BBKCsYCyOuL$S6fPdSxN4cWBR6 z^pA&B&QL2d129YLR%7Nvm>BSh+V=tdqhLjC!W!J5xvD00`sa8qiGdu9Bv^d2oHv-C z(I$gkbU1S;YnDmt+#lehBO`VVBQR%8baAkE9e5s(%sYQ*ANYXp9?V2!F3goSc%$nn z_RIm)L@*T#f()qNq3w*PQOl;78}0rMm7m}BboU5yPOzx5$K;-5(a7uzwpbYh3du(%%v~x%G@hkwUne~<^US~{*+;E| z_2p%{8yX_DmuGpoe_zu1!}9nBe+pn>%78-{c%C}BFMY@}GuWrO6MqSZed80}1|Fd{ zFMk*V%PM_e+LCm>{}^m0{|V;KS6HWy?TkBil{UWu>b+*;GrzQ0{pEkN*ZGfMJ^k*y z`>tnLe*W3yGmrbNKj$RAYnKGhAAN>fpM;33o!>5OiKTksx*S<|S*SI_EUH`I@F81` z93MTqZ-}^RNZeXHLx4htT>*k+Qf6rf#=q-dl?|EjuH`*2Q#}1n3fiQ*9 zPSfxSLu0wZ$;V4(&W)k=A+bFo>`;7rJS0ES+B^~Hs`zz2-Fz}*08Ng2>TEP6CxKOdaRa2<(WZzD$!p;Xw6CI znH!o%DX7Mhp}L1;*bP<*zosi)iC!pz6=74?*lv5~L4R})jSR$>_Tc=3cES`0$-J5f z3Y$~DW-QT2KuEMSPXWF;oB9qPmL8@cQz}CS9{c$QA$vRcQy4``B2RyUHiRCzyBiLa7pMvcCPIG`V#3(i@6E98Ck4zN!k;X^pejBv0; z^6AIHB!EDneA=T=g^|pBmW|SWqR$!~V3v47GOQWddUH)5YrcbK2lFQh#SYPlu3^Xw z|J(Gu70QUyKZBQZGH4i(|3ao!2G^C!V1_?8fMp2}c&vb;pCXY0riD|uXMzT;fM)X#seMg2-V z1XDi|0|+SfhCcwDV-6#Q;c%>B?AB-t><4m0g6ugtP~Fx*aD^G2POB(;iOE|NLeBG1 zWadirG+GV&phCr1+kJSwN=0uWNI_Gg*oaXM$`SSDANB81*4U@&ZA{ zhB1W)T1@J^ z5{DbESpn63&Ozy-L43|{8ICJ`*F4jO-gF+fS+mXM1?^Kn<-9;>#QJmoFElY^T;c87$fhA}5d!}OXyBII_p#*D8! zt9o;^Hp~G7Pb3r%;hPUQqvrr5=6Ny-vpUQnHO=lHUNYsb+3bhncRq}2m=*V4hPL!s zp7P!`NdIT0a&LO`OWV7i%bsXD6LRH?%DL)2C_XBzaWdq&`paYNDz%W(E=MZQOXE_WdCP~NUM5Jp%1>b}Ob1|=N2RX){Z0Gq zD>uKi-E#KH__m48d(#TP?eu-=SJn;pz7r$H>HyC8h!Y6FTaZ(eAhE9lYZxaBPtkM0H}b~ z%2H(^yYQ5ls6lbjdihn5mz$VTO2S5D@k-dZh--$$`|=?p3pfi|L>dGkMqtwe@tMXe z`Ao=frA@B+f^iD+((gqiVmT6pUfc$@QpCP)p4-u-)RdBTg;BBuVJ2fC+9A#$+dl)$VTN3mEKJ24p9agOH5j0fYrX z$s@r*PC!CD@Pv4e^GtVQMhhkilA87R-#Qc$6zJ6ELPQ z^aF$*!;ZRZZ?q#t6g3-Nq8jN#(ZeLf7Rz9};O;T+>her>u;cj>?n?5{a~cMGwB;rv zX|b`lTmb2=vBHf^hT&IDpYbQXb?q@H(d^1`u^eZwIOE2jaP&|}1p~ESv1r$dbk zWh*$eH~|q>8EpzU4`c12lF^!>kuqHJ4$ex#Ds&W=2HDuT0SeTV zX@Uq`)EK!tE-iC2M$N{Og2@5o=q|w_jE=U&JdEMZn6`MPxtm7?DUh$xj%fm+Nep6fFv#I(K>U4<)zP3H{ zsX{0Rek11%T8PUwPT6Q`R={rE!(425!Wb~l0+esf!}bVt_^n54VrX9R#c^7)>px?z zZp0tjyP6x>4}1U~YH?a<@)S4?j3I0~qJy0P#W?ZFFRQG^Sp!2-?7 z7BeZwbBI>PAPIBVOdGz%emR;><6#mr8O2@O0=n(=b9jRHNfbHQhdm=4{r4&=!Ar z>$%KBG(pElIOpU0^AevD&$LNEkijAEMme-{%TC6{_5wU*e1X%ldWnEdm}7*b;ef+H znddV!!iLV?G0r_E=mt10=-0dNy;~iO@HLwvlryI_oAof;%ZpRs)vJyP$}j*wdJNjT z12jC~`Hr#_io1-T&oyZb{?pUti1TLf?dmKrBV+ux!Wv*p#-#a>#d0x+v%XNG=O#%N zU6=EWd)HdWE6(A(b510`f4ux6o%rlpZAZ~Z}S3c=epWx z>4Aa@+Z{>E1WUWaPe>$kfdl5}Pa`z>IL)em_|Cd=QCazEt>w9I%3aAvNr6@K-tXpe z-D_T!a#&v}pviCEX8jJlEu4g9RW+3#;RPgUSlY(3r0}M`ZrjWK6Fz^Cx%LlS;2!ve z&b4#e8~#I@`F#((slTPoliGh`umA3R{-@Ede6Of}htdxN9B;4)eU9*`&7=(BVWvvJ zisLquGLy7SX(zFh)(IJQ$Fwjh_a~^ooBI$2^2D@)Le1lDO2Z7vj5n8c%cM&%3M(Z+ zfGF~;DUTn}n?mG#CUNxM^{lk;9*lWV@|OBTtf=gjdMHE2L!rZumwCLJg&;Q$oPFg} zSOMzfw^v{mDxTZ?DFRSgOv;0j))wnD9@>_^lsuZnl`9mIvh7fTvoJEZ4%PX=bD4Cq z^Ql)ZUKQe52yS4+d*Ns^BY={j5MGEhi-+T;gMEA)=#}OfR=Yrup;HkU+@-!jfSKbs z-+8eB06+jqL_t))vh>^(GzE#w#NzGh(?Uq;PR7;I2~m<@ygck|bc~5s%q_I*XZ&Oa zX@|R0YZ#L&nAH^wm!>sr^rKnOZJ3H-06g6t_5v*ocI{{+uH50pG|fd$W>}mwx{izo z+%nO3*cpdO9UiJ#(S!xYp7wk0OIyZ*k6NZZW@a6C&|w~ou+?JMb`R5`GqomZa`Bo$ zc@%m%D6ud~I$x^4P)GnVVE)7vdPARbLOSghPjtdxiMzl&_>15o9%*VPV?X3fh8)z# zd+dn9q``%tQG$^KlsfkFADH9M1B{nwmV}{V*9L}JhTZ5-rlmHRA=<^RuFQsK(Foxd z40#X6O2__|jHa9@4RwlH_I`C;O&Nn7JIXgO#A>s;oCP5>j@(h^C~$Um0czkx%hN7k zfi^^Z@$Cx1Kw(qOtV~iPsKZ5rYh<(x&HB(7=-0&b;(|F-D5Os^E^7o^+v!;~c$LsN zl4KrA8_>MK>}jUA+j1g2Dcqgc1Wci9i4+j0d)lplZMbw2!;I4|IwJENEfMLLZZ>2B zNGBK{AnLenK}!2fBWM?)e8K%bjC3DneZD}mvjP|GKiV4wY>C4F{L-|`xuHpwW}-_Z zW=~}IJo_P`id*dBYaXV+utgZ3p^-o&pj_~paKHnY=GN>b=6PrnFbx_V!t6sk)UdV? z*fdpCL+QNG1kcSA{gGri4WB(EOxIq)5!@B7_AUN9w9ENrkct)U+NqTSsG7bhNb5-c z+M7n}6Q(!?j~Qj!=(#p=)CR{d(G)lZgq`@Mc4O}$u*?zt6(fAYd?rd5360WqhEK

    +

    + +### Code Example – Installing for production with multi-stage build + +
    + +Dockerfile + +``` +FROM node:14.8.0-alpine AS build +COPY --chown=node:node package.json package-lock.json ./ +# ✅ Safe install +RUN npm ci +COPY --chown=node:node src ./src +RUN npm run build + +# Run-time stage +FROM node:14.8.0-alpine +COPY --chown=node:node --from=build package.json package-lock.json ./ +COPY --chown=node:node --from=build node_modules ./node_modules +COPY --chown=node:node --from=build dist ./dist + +# ✅ Clean dev packages +RUN npm prune --production + +CMD [ "node", "dist/app.js" ] +``` + +
    + +

    ### Code Example Anti-Pattern – Installing all dependencies From 5062025381c0d75abd73cef37190f33b12125c10 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Aug 2020 10:51:13 +0300 Subject: [PATCH 0377/1795] Update README.md --- README.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2e5e35218..317376ed4 100644 --- a/README.md +++ b/README.md @@ -1144,7 +1144,7 @@ CMD [ "node", "dist/app.js" ] **TL;DR:** Although DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' -**Otherwise:** Many of the infamous npm security breaches were found within development packages +**Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) 🔗 Read More: [Remove development dependencies](/sections/docker/install-for-production.md) @@ -1182,7 +1182,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.9. Use explicit image reference, avoid `latest` tag -**TL;DR:** The `latest` tag can be misleading and is subject to much confusion. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. +**TL;DR:** Specify an explicit image digest or versioned label, never refer to 'latest'. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. @@ -1194,7 +1194,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.10. Prefer smaller Docker base images -**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Alpine Linux variants, mitigates this issue. +**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. **Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. @@ -1220,6 +1220,8 @@ In addition, referring to an image tag means that the base image is subject to c 🔗 [**Read More: Generic Docker practices**](/sections/docker/scan-images.md) +


    + ## ![✔] 8.13 Clean NODE_MODULE cache **TL;DR:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off @@ -1228,6 +1230,8 @@ In addition, referring to an image tag means that the base image is subject to c 🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) +


    + ## ![✔] 8.14. Generic Docker practices **TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. From bdd823273eda50e29c73a503075fac0b8c9c1232 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Thu, 20 Aug 2020 11:30:16 +0200 Subject: [PATCH 0378/1795] Fix links in markdown, change quotes type and add a word or two on docker section --- README.md | 4 ++-- sections/docker/graceful-shutdown.md | 2 +- sections/docker/install-for-production.md | 4 ++-- sections/docker/multi_stage_builds.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 317376ed4..fd68e3702 100644 --- a/README.md +++ b/README.md @@ -1142,7 +1142,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.5. Clean-up dependencies before production -**TL;DR:** Although DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' +**TL;DR:** Although DevDependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` **Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) @@ -1182,7 +1182,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.9. Use explicit image reference, avoid `latest` tag -**TL;DR:** Specify an explicit image digest or versioned label, never refer to 'latest'. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. +**TL;DR:** Specify an explicit image digest or versioned label, never refer to `latest`. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. diff --git a/sections/docker/graceful-shutdown.md b/sections/docker/graceful-shutdown.md index 52c889860..471989bba 100644 --- a/sections/docker/graceful-shutdown.md +++ b/sections/docker/graceful-shutdown.md @@ -9,7 +9,7 @@ In a Dockerized runtime like Kubernetes, containers are born and die frequently.

    -### Code Example – Placing Node.js as the root process allows passing signals to the code (see [/sections/docker/bootstrap-using-node.md]) +### Code Example – Placing Node.js as the root process allows passing signals to the code (see [bootstrap using node](/sections/docker/bootstrap-using-node.md))
    diff --git a/sections/docker/install-for-production.md b/sections/docker/install-for-production.md index d6cead3aa..51ec0e7bc 100644 --- a/sections/docker/install-for-production.md +++ b/sections/docker/install-for-production.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependecies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons the image that is finally shipped to production should be safe and minimal. Running npm install with a `--production` is a great start, however it gets even safer to run 'npm ci' that ensures a fresh install and the existence of a lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, multi stage builds can help in having different sets of dependencies and finally only those for production (See dedicated bullet on multi stage builds) +Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependencies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons the image that is finally shipped to production should be safe and minimal. Running npm install with a `--production` is a great start, however it gets even safer to run `npm ci` that ensures a fresh install and the existence of a lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, [multi stage builds](/sections/docker/multi_stage_builds.md) can help in having different sets of dependencies and finally only those for production.

    @@ -58,7 +58,7 @@ CMD [ "node", "dist/app.js" ]

    -### Code Example Anti-Pattern – Installing all dependencies +### Code Example Anti-Pattern – Installing all dependencies in a single stage dockerfile
    diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md index e3ee692e7..78328c4d3 100644 --- a/sections/docker/multi_stage_builds.md +++ b/sections/docker/multi_stage_builds.md @@ -2,7 +2,7 @@ ### One Paragraph Explainer -Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime (see [/sections/docker/avoid-build-time-secrets.md]), such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). +Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime (see [avoid build time secrets](/sections/docker/avoid-build-time-secrets.md)), such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). ### Example From b23fcf2c0654a74d365c624843501f003221ca6f Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Wed, 19 Aug 2020 10:20:45 +0200 Subject: [PATCH 0379/1795] Add an example project with dockerfile - 1st draft --- sections/examples/dockerfile/.dockerignore | 13 +++++++++ sections/examples/dockerfile/.npmrc | 0 sections/examples/dockerfile/Dockerfile | 31 ++++++++++++++++++++++ sections/examples/dockerfile/package.json | 28 +++++++++++++++++++ sections/examples/dockerfile/src/app.ts | 10 +++++++ 5 files changed, 82 insertions(+) create mode 100644 sections/examples/dockerfile/.dockerignore create mode 100644 sections/examples/dockerfile/.npmrc create mode 100644 sections/examples/dockerfile/Dockerfile create mode 100644 sections/examples/dockerfile/package.json create mode 100644 sections/examples/dockerfile/src/app.ts diff --git a/sections/examples/dockerfile/.dockerignore b/sections/examples/dockerfile/.dockerignore new file mode 100644 index 000000000..9d284936d --- /dev/null +++ b/sections/examples/dockerfile/.dockerignore @@ -0,0 +1,13 @@ +node_modules/ +.git +README.md +LICENSE +.vscode +.idea +npm-debug.log +coverage +.env +.editorconfig +.aws +dist +.npmrc diff --git a/sections/examples/dockerfile/.npmrc b/sections/examples/dockerfile/.npmrc new file mode 100644 index 000000000..e69de29bb diff --git a/sections/examples/dockerfile/Dockerfile b/sections/examples/dockerfile/Dockerfile new file mode 100644 index 000000000..941f035e9 --- /dev/null +++ b/sections/examples/dockerfile/Dockerfile @@ -0,0 +1,31 @@ +FROM node:14.8.0-alpine AS build + +# Copy dependency information and install all dependencies +COPY --chown=node:node package.json package-lock.json ./ + +RUN npm ci + +# Copy source code (and all other relevant files) +COPY --chown=node:node src ./src + +# Build code +RUN npm run build + +# Run-time stage +FROM node:14.8.0-alpine + +# Set non-root user and expose port 3000 +USER node +EXPOSE 3000 + +WORKDIR /home/node/app + +# Copy dependency information and build results from previous stage +COPY --chown=node:node --from=build package.json package-lock.json ./ +COPY --chown=node:node --from=build node_modules ./node_modules +COPY --chown=node:node --from=build dist ./dist + +# Clean dev packages +RUN npm prune --production + +CMD [ "node", "dist/app.js" ] diff --git a/sections/examples/dockerfile/package.json b/sections/examples/dockerfile/package.json new file mode 100644 index 000000000..939445686 --- /dev/null +++ b/sections/examples/dockerfile/package.json @@ -0,0 +1,28 @@ +{ + "name": "node-app-with-docker", + "version": "1.0.0", + "description": "An example node app that uses docker to be built", + "main": "src/app.ts", + "scripts": { + "run": "dist/app.js", + "build": "tsc --outDir dist -m commonjs -t ES2020 src/app.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/goldbergyoni/nodebestpractices.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/goldbergyoni/nodebestpractices/issues" + }, + "homepage": "https://github.com/goldbergyoni/nodebestpractices#readme", + "dependencies": { + "koa": "^2.13.0" + }, + "devDependencies": { + "@types/koa": "^2.11.4", + "typescript": "^3.9.7" + } +} diff --git a/sections/examples/dockerfile/src/app.ts b/sections/examples/dockerfile/src/app.ts new file mode 100644 index 000000000..d51890c18 --- /dev/null +++ b/sections/examples/dockerfile/src/app.ts @@ -0,0 +1,10 @@ +import * as Koa from 'koa'; +const app = new Koa(); + +app.use(async ctx => { + ctx.body = 'Hello World'; +}); + +app.listen(3000, () => { + console.log('Navigate to http://localhost:3000'); +}); From 1fda1a9ec3d29e360a3fce62f2ab09869389ddd2 Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Thu, 20 Aug 2020 10:49:43 +0200 Subject: [PATCH 0380/1795] Update Dockerfile and switch with express --- sections/examples/dockerfile/Dockerfile | 23 +++++++++++++++++------ sections/examples/dockerfile/package.json | 6 +++--- sections/examples/dockerfile/src/app.ts | 10 +++++----- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/sections/examples/dockerfile/Dockerfile b/sections/examples/dockerfile/Dockerfile index 941f035e9..19d89f70a 100644 --- a/sections/examples/dockerfile/Dockerfile +++ b/sections/examples/dockerfile/Dockerfile @@ -1,8 +1,17 @@ +# This is a multistage Dockerfile. +# In the first stage we install system build dependencies, copy project files and build them +# In the second stage, we start fresh and only copy necessary files. We also purge node_modules devDependencies. + +#### Build stage #### FROM node:14.8.0-alpine AS build -# Copy dependency information and install all dependencies +# Install system build dependencies (if needed) at the top ✅ See bullet point #8.8 about caching +RUN apk add --update --no-cache bash make gcc g++ lcms2-dev libpng-dev autoconf automake + +# Only copy node dependency information and install all dependencies first COPY --chown=node:node package.json package-lock.json ./ +# Install packages using the lockfiles as source of truth ✅ See bullet point #8.5 about npm ci RUN npm ci # Copy source code (and all other relevant files) @@ -11,8 +20,9 @@ COPY --chown=node:node src ./src # Build code RUN npm run build -# Run-time stage -FROM node:14.8.0-alpine +#### Run-time stage #### +# ✅ See bullet point #8.10 about smaller docker base images +FROM node:14.8.0-alpine as app # Set non-root user and expose port 3000 USER node @@ -20,12 +30,13 @@ EXPOSE 3000 WORKDIR /home/node/app -# Copy dependency information and build results from previous stage +# Copy dependency information and build output from previous stage COPY --chown=node:node --from=build package.json package-lock.json ./ COPY --chown=node:node --from=build node_modules ./node_modules COPY --chown=node:node --from=build dist ./dist -# Clean dev packages -RUN npm prune --production +# Clean dev dependencies ✅ See bullet point #8.5 +RUN npm prune --production && npm cache clean --force +# ✅ See bullet point #8.2 about avoiding npm start CMD [ "node", "dist/app.js" ] diff --git a/sections/examples/dockerfile/package.json b/sections/examples/dockerfile/package.json index 939445686..2c9330e4f 100644 --- a/sections/examples/dockerfile/package.json +++ b/sections/examples/dockerfile/package.json @@ -4,7 +4,7 @@ "description": "An example node app that uses docker to be built", "main": "src/app.ts", "scripts": { - "run": "dist/app.js", + "start": "node dist/app.js", "build": "tsc --outDir dist -m commonjs -t ES2020 src/app.ts", "test": "echo \"Error: no test specified\" && exit 1" }, @@ -19,10 +19,10 @@ }, "homepage": "https://github.com/goldbergyoni/nodebestpractices#readme", "dependencies": { - "koa": "^2.13.0" + "express": "^4.17.1" }, "devDependencies": { - "@types/koa": "^2.11.4", + "@types/express": "^4.17.7", "typescript": "^3.9.7" } } diff --git a/sections/examples/dockerfile/src/app.ts b/sections/examples/dockerfile/src/app.ts index d51890c18..93131f2e9 100644 --- a/sections/examples/dockerfile/src/app.ts +++ b/sections/examples/dockerfile/src/app.ts @@ -1,9 +1,9 @@ -import * as Koa from 'koa'; -const app = new Koa(); +import * as express from 'express'; +const app = express(); -app.use(async ctx => { - ctx.body = 'Hello World'; -}); +app.get('/', (req, res) => { + res.send('Hello World!') +}) app.listen(3000, () => { console.log('Navigate to http://localhost:3000'); From 129dc803f180089e3de1780145e4c962aeac334a Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Aug 2020 14:16:11 +0300 Subject: [PATCH 0381/1795] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 317376ed4..54eb62429 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines - **:whale: Node.js + Docker best practices**: We've just release the Docker with Node.js section which includes 15 bullets about better coding techqniues with Docker +- **🎤 A talk at OdessaJS**: We will speak about Node.js testing this week at the great [OdessaJS conference](https://odessajs.org/) +

    # Welcome! 3 Things You Ought To Know First From 1ec1a0ce569e105594c00e0cfda0ff90de0fa3ae Mon Sep 17 00:00:00 2001 From: Siddharth Goel Date: Fri, 21 Aug 2020 00:34:23 +0530 Subject: [PATCH 0382/1795] removed the comma before and as mentioned in PR review --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c29497da1..6580aaaad 100644 --- a/README.md +++ b/README.md @@ -1103,7 +1103,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.3. Remove development dependencies -**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped, and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' +**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running 'npm ci --production' **Otherwise:** Many of the infamous npm security breaches were found within development packages From a2d89778ffc71f5cbe49ceb6929bae3cfaafc9a2 Mon Sep 17 00:00:00 2001 From: Jimmy Callin Date: Sat, 22 Aug 2020 10:55:23 +0200 Subject: [PATCH 0383/1795] Fix npm cache clean command --- sections/docker/clean-cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/docker/clean-cache.md b/sections/docker/clean-cache.md index 70669f747..485d074d6 100644 --- a/sections/docker/clean-cache.md +++ b/sections/docker/clean-cache.md @@ -19,7 +19,7 @@ Node package managers, npm & Yarn, cache the installed packages locally so that FROM node:12-slim AS build WORKDIR /usr/src/app COPY package.json package-lock.json ./ -RUN npm ci --production && npm clean cache --force +RUN npm ci --production && npm cache clean --force # The rest comes here ``` From 6dfcc8ba75543ec2a996b3c5abd80040d7f39681 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Sun, 23 Aug 2020 10:02:59 +0300 Subject: [PATCH 0384/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54eb62429..50218a37e 100644 --- a/README.md +++ b/README.md @@ -1172,7 +1172,7 @@ CMD [ "node", "dist/app.js" ]


    -## ![✔] 8.8. Caching +## ![✔] 8.8. Plan for efficient caching **TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. From de5ad75fe5ca87bde5053ae43b8c898d0835c51e Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Sun, 23 Aug 2020 10:03:28 +0300 Subject: [PATCH 0385/1795] Update clean-cache.md --- sections/docker/clean-cache.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/docker/clean-cache.md b/sections/docker/clean-cache.md index 70669f747..485d074d6 100644 --- a/sections/docker/clean-cache.md +++ b/sections/docker/clean-cache.md @@ -19,7 +19,7 @@ Node package managers, npm & Yarn, cache the installed packages locally so that FROM node:12-slim AS build WORKDIR /usr/src/app COPY package.json package-lock.json ./ -RUN npm ci --production && npm clean cache --force +RUN npm ci --production && npm cache clean --force # The rest comes here ``` From 814e904f38848c5fc36f1f905935b4e8524f8e1d Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Sun, 23 Aug 2020 10:21:52 +0300 Subject: [PATCH 0386/1795] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 50218a37e..59d873df6 100644 --- a/README.md +++ b/README.md @@ -72,11 +72,11 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines

    -## ![✔] 1.2 Layer your components, keep Express within its boundaries +## ![✔] 1.2 Layer your components, keep the web layer within its boundaries -**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic, and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (Express req, res) to business logic and data layers - this makes your application dependent on and accessible by Express only +**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic, and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (e.g. Express req, res) to business logic and data layers - this makes your application dependent on and accessible only by specific web frameworks -**Otherwise:** App that mixes web objects with other layers cannot be accessed by testing code, CRON jobs, and other non-Express callers +**Otherwise:** App that mixes web objects with other layers cannot be accessed by testing code, CRON jobs, triggers from message queues, etc 🔗 [**Read More: layer your app**](/sections/projectstructre/createlayers.md) @@ -146,7 +146,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines

    -## ![✔] 2.4 Handle errors centrally, not within an Express middleware +## ![✔] 2.4 Handle errors centrally, not within a middleware **TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in @@ -218,7 +218,7 @@ Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chines ## ![✔] 2.11 Fail fast, validate arguments using a dedicated library -**TL;DR:** This should be part of your Express best practices – Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like Joi +**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like [ajv](https://www.npmjs.com/package/ajv) and [Joi](https://www.npmjs.com/package/joi) **Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? From d8e733d68b331893aa9967382dcdec0236aba120 Mon Sep 17 00:00:00 2001 From: Michael Solomon Date: Sun, 23 Aug 2020 17:32:09 +0300 Subject: [PATCH 0387/1795] Update memory-limit.md --- sections/docker/memory-limit.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sections/docker/memory-limit.md b/sections/docker/memory-limit.md index 16af2df40..f624d018b 100644 --- a/sections/docker/memory-limit.md +++ b/sections/docker/memory-limit.md @@ -1,10 +1,10 @@ -# Set memory limits using Docker only +# Set memory limits using both Docker and v8

    ### One Paragraph Explainer -A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink all the juice alone and leaves other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as it has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, with Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-10% of Docker's memory limit. +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink all the juice alone and leaves other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as it has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, with Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-100% of Docker's memory limit.

    From 27f0d5121948df15a2a9ecfbd3fbc5569cbb8577 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2020 19:39:02 +0000 Subject: [PATCH 0388/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 59d873df6..fb1b776f9 100644 --- a/README.md +++ b/README.md @@ -1504,6 +1504,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Or Bin

    🖋
    Andreo Vieira

    🖋 +
    Jimmy Callin

    🖋 From 2cfa8a7dbe22769d1fc9d2a1cd00bcf237a1d777 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2020 19:39:03 +0000 Subject: [PATCH 0389/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d9fc5005c..adf12c93d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -969,6 +969,15 @@ "contributions": [ "content" ] + }, + { + "login": "jimmycallin", + "name": "Jimmy Callin", + "avatar_url": "https://avatars0.githubusercontent.com/u/2225828?v=4", + "profile": "https://github.com/jimmycallin", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 3180bedf9001da66d367bd99c2a95485cac75bf0 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2020 19:40:21 +0000 Subject: [PATCH 0390/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 59d873df6..217a4f217 100644 --- a/README.md +++ b/README.md @@ -1504,6 +1504,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Or Bin

    🖋
    Andreo Vieira

    🖋 +
    Michael Solomon

    🖋 From b55c4e6d5f86685089f2ad14946ddffa19f66438 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 23 Aug 2020 19:40:22 +0000 Subject: [PATCH 0391/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index d9fc5005c..f7cd9cee3 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -969,6 +969,15 @@ "contributions": [ "content" ] + }, + { + "login": "mikicho", + "name": "Michael Solomon", + "avatar_url": "https://avatars1.githubusercontent.com/u/11459632?v=4", + "profile": "https://github.com/mikicho", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From e74183fd5b43ca5a6b0d0c50b6d2ba21b40c4aee Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 24 Aug 2020 10:37:35 +0300 Subject: [PATCH 0392/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 913f4304e..fa0b6528d 100644 --- a/README.md +++ b/README.md @@ -1164,7 +1164,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.7. Set memory limits using both Docker and v8 -**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags: Set the v8's old space memory to be a bit less than the container limit +**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit **Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources From d77eb0c05336b63446075fad6f808800ccff09cb Mon Sep 17 00:00:00 2001 From: Kevyn Bruyere Date: Mon, 24 Aug 2020 14:40:36 +0200 Subject: [PATCH 0393/1795] Add missing `zombie` word in 8.2 BP --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fa0b6528d..894828f80 100644 --- a/README.md +++ b/README.md @@ -1114,7 +1114,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.2. Bootstrap using 'node' command, avoid npm start -**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-process, signal handling, graceful shutdown and having processes. +**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-process, signal handling, graceful shutdown and having zombie processes. **Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data. From 0a9a8b7c8e46abe397839570cfa0ebf8b7d45213 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 05:34:46 +0000 Subject: [PATCH 0394/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2ecde843b..c81012b5a 100644 --- a/README.md +++ b/README.md @@ -1506,6 +1506,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Andreo Vieira

    🖋
    Michael Solomon

    🖋
    Jimmy Callin

    🖋 +
    Siddharth

    🖋 From 080845e579911a599a06bdf32c94c7cb2d51e7aa Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 25 Aug 2020 05:34:47 +0000 Subject: [PATCH 0395/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 126a23951..b5487861f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -975,7 +975,7 @@ "name": "Michael Solomon", "avatar_url": "https://avatars1.githubusercontent.com/u/11459632?v=4", "profile": "https://github.com/mikicho", - "contributions": [ + "contributions": [ "content" ] }, @@ -987,6 +987,15 @@ "contributions": [ "content" ] + }, + { + "login": "w01fS", + "name": "Siddharth", + "avatar_url": "https://avatars2.githubusercontent.com/u/26025955?v=4", + "profile": "https://www.linkedin.com/in/siddharthofficial/", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 642dc29ac86697950f4854eb0cc3156701ab1fde Mon Sep 17 00:00:00 2001 From: Tom Boettger <49961674+bttger@users.noreply.github.com> Date: Wed, 26 Aug 2020 23:33:24 +0200 Subject: [PATCH 0396/1795] Use npm ci command in Docker builds --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c81012b5a..28f0e2774 100644 --- a/README.md +++ b/README.md @@ -1085,7 +1085,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. Since Docker is often used in continous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. @@ -1095,7 +1095,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E FROM node:14.4.0 AS build COPY . . -RUN npm install && npm run build +RUN npm ci && npm run build FROM node:slim-14.4.0 @@ -1103,7 +1103,7 @@ USER node EXPOSE 8080 COPY --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/package-lock.json ./ -RUN npm install --production +RUN npm ci --production CMD [ "node", "dist/app.js" ] ``` From 441519a1b79a89e207e8e2fc56274a7c1df39bc4 Mon Sep 17 00:00:00 2001 From: Tom Boettger <49961674+bttger@users.noreply.github.com> Date: Wed, 26 Aug 2020 23:52:37 +0200 Subject: [PATCH 0397/1795] Add hint to use npm ci in automated environments npm ci command should be used within automated environments such as continuous integration pipelines https://docs.npmjs.com/cli/ci.html#description --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 28f0e2774..d63bd69fa 100644 --- a/README.md +++ b/README.md @@ -734,9 +734,9 @@ All statements above will return false if used with `===`

    -## ![✔] 5.19. Install your packages with `npm ci` +## ![✔] 5.19. Install your packages with `npm ci` -**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to do a clean install of your dependencies matching package.json and package-lock.json. +**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to do a clean install of your dependencies matching package.json and package-lock.json. Using this command is recommended in automated environments such as continuous integration pipelines. It is stricter by throwing an error if the versions in package.json and package-lock.json do not match. Since it can be significantly faster than `npm install`, it can also save you CI pipeline minutes. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. **Otherwise:****** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code @@ -1085,7 +1085,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. Since Docker is often used in continous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. Since Docker is often used in continuous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. From 74f47c8d66d2985c70d6cc107cbd8a01035c36fa Mon Sep 17 00:00:00 2001 From: Tom Boettger Date: Fri, 28 Aug 2020 00:59:32 +0200 Subject: [PATCH 0398/1795] Update the multi_stage_builds section with npm ci --- README.md | 2 +- sections/docker/multi_stage_builds.md | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d63bd69fa..98c8c0e86 100644 --- a/README.md +++ b/README.md @@ -1085,7 +1085,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. Since Docker is often used in continuous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. diff --git a/sections/docker/multi_stage_builds.md b/sections/docker/multi_stage_builds.md index 78328c4d3..eccba3bf0 100644 --- a/sections/docker/multi_stage_builds.md +++ b/sections/docker/multi_stage_builds.md @@ -31,11 +31,13 @@ docs #### Dockerfile with multiple stages +Since Docker is often used in continuous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. This example uses yarn as package manager for which the equivalent to `npm ci` is the `yarn install --frozen-lockfile` [command](https://classic.yarnpkg.com/en/docs/cli/install/). + ```dockerfile FROM node:14.4.0 AS build COPY --chown=node:node . . -RUN yarn install && yarn build +RUN yarn install --frozen-lockfile && yarn build FROM node:14.4.0 @@ -44,7 +46,7 @@ EXPOSE 8080 # Copy results from previous stage COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ -RUN yarn install --production +RUN yarn install --frozen-lockfile --production CMD [ "node", "dist/app.js" ] ``` @@ -55,7 +57,7 @@ CMD [ "node", "dist/app.js" ] FROM node:14.4.0 AS build COPY --chown=node:node . . -RUN yarn install && yarn build +RUN yarn install --frozen-lockfile && yarn build # This will use a minimal base image for the runtime FROM node:14.4.0-alpine @@ -65,7 +67,7 @@ EXPOSE 8080 # Copy results from previous stage COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ -RUN yarn install --production +RUN yarn install --frozen-lockfile --production CMD [ "node", "dist/app.js" ] ``` From 1a666db2d267818af584244412d8c3220e3c44be Mon Sep 17 00:00:00 2001 From: Tom Boettger Date: Sun, 30 Aug 2020 23:58:08 +0200 Subject: [PATCH 0399/1795] Shorten the TL;DR --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98c8c0e86..77b3578d8 100644 --- a/README.md +++ b/README.md @@ -736,9 +736,9 @@ All statements above will return false if used with `===` ## ![✔] 5.19. Install your packages with `npm ci` -**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to do a clean install of your dependencies matching package.json and package-lock.json. Using this command is recommended in automated environments such as continuous integration pipelines. It is stricter by throwing an error if the versions in package.json and package-lock.json do not match. Since it can be significantly faster than `npm install`, it can also save you CI pipeline minutes. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. +**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Using this command is recommended in automated environments such as continuous integration pipelines. -**Otherwise:****** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. 🔗 [**Read More: Use npm ci**](/sections/production/installpackageswithnpmci.md) From d9b1275b841d79750ef9e0b8aeb22f5197254a45 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2020 10:21:33 +0000 Subject: [PATCH 0400/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c81012b5a..cd5e032bf 100644 --- a/README.md +++ b/README.md @@ -1507,6 +1507,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Michael Solomon

    🖋
    Jimmy Callin

    🖋
    Siddharth

    🖋 +
    Ryan Smith

    🖋 From a394f7903e35f6ad46ab36de864c8c33b46a8e1c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2020 10:21:35 +0000 Subject: [PATCH 0401/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index b5487861f..abf5ac0aa 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -996,6 +996,15 @@ "contributions": [ "content" ] + }, + { + "login": "ryan3E0", + "name": "Ryan Smith", + "avatar_url": "https://avatars0.githubusercontent.com/u/1578766?v=4", + "profile": "https://ryansmith.tech/", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From a9a9bfd93068c01decf581992bcba68f2adf7f13 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2020 12:21:35 +0000 Subject: [PATCH 0402/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 44cf8eda5..035e79736 100644 --- a/README.md +++ b/README.md @@ -1508,6 +1508,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Jimmy Callin

    🖋
    Siddharth

    🖋
    Ryan Smith

    🖋 +
    Tom Boettger

    🖋 From ea61a43cb9b428b31a764089f3bfc85e4b8d82f2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2020 12:21:37 +0000 Subject: [PATCH 0403/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index abf5ac0aa..76f817336 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1005,6 +1005,15 @@ "contributions": [ "content" ] + }, + { + "login": "bttger", + "name": "Tom Boettger", + "avatar_url": "https://avatars2.githubusercontent.com/u/49961674?v=4", + "profile": "https://de.linkedin.com/in/tom-boettger", + "contributions": [ + "content" + ] } ], "projectName": "nodebestpractices", From 891a8c4c5ec51bd254117be934c17159f74442b8 Mon Sep 17 00:00:00 2001 From: Fathur Rahman Date: Mon, 28 Sep 2020 10:46:09 +0700 Subject: [PATCH 0404/1795] Create writing-guidelines.indonesia.md --- .operations/writing-guidelines.indonesia.md | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .operations/writing-guidelines.indonesia.md diff --git a/.operations/writing-guidelines.indonesia.md b/.operations/writing-guidelines.indonesia.md new file mode 100644 index 000000000..3b65f7580 --- /dev/null +++ b/.operations/writing-guidelines.indonesia.md @@ -0,0 +1,31 @@ +# Manifes penulisan konten kami + +Bagaimana kami meningkatkan pengalaman membaca dan belajar bagi pengunjung kami. + +## 1. Sederhana lebih baik daripada lebih baik + +Memudahkan membaca dan menyerap pengetahuan adalah misi kami, kami mengurasi konten. Karena itu, kami fokus pada mengubah topik yang kompleks dan melelahkan menjadi daftar yang disederhanakan, memperdagangkan informasi yang kelebihan beban dengan detail yang dipersingkat dan kurang akurat, menghindari topik yang 'mudah terbakar' dan kontroversial, serta menghindari ide subjektif yang mendukung praktik yang diterima secara umum. + +## 2. Berbasis bukti dan dapat diandalkan + +Pembaca kami harus yakin bahwa konten yang mereka baca dapat diandalkan. Kami mencapai ini dengan memasukkan bukti seperti referensi, data, dan sumber daya lain yang tersedia untuk topik ini. Secara praktis, upayakan untuk memasukkan kutipan dari sumber yang dapat dipercaya, menunjukkan tolok ukur, pola desain terkait, atau ukuran ilmiah apa pun untuk membuktikan klaim Anda. + +## 3. MECE (Saling Eksklusif dan Secara Kolektif) + +Selain konten yang sangat diedit dan dapat diandalkan, membaca sekilas konten juga harus memberikan cakupan topik yang lengkap. Tidak ada sub-topik penting yang harus ditinggalkan. + +## 4. Pemformatan yang konsisten + +Konten disajikan menggunakan templat tetap. Setiap konten di masa mendatang harus sesuai dengan template yang sama. Jika Anda ingin menambahkan poin baru, salin format poin dari poin yang ada dan kembangkan sesuai kebutuhan Anda. Untuk informasi tambahan, silakan lihat [template ini] (https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md). + +## 5. Ini Tentang Node.js + +Setiap saran harus terkait langsung dengan Node.js dan tidak dengan pengembangan perangkat lunak secara umum. Saat kami menyarankan untuk menerapkan pola / aturan umum di Node.js, konten harus fokus pada implementasi Node. Misalnya, ketika kami menyarankan untuk membersihkan semua input permintaan untuk alasan keamanan, Node-lingo harus digunakan - ‘Gunakan middleware untuk membersihkan input permintaan’. Jika sebuah item tidak memiliki implementasi khusus di Node.js (misalnya, item tersebut terlihat sama di Python & Jaba) - sertakan di dalam item container umum, lihat item 6.5 misalnya. + +## 6. Hanya vendor terkemuka + +Terkadang berguna untuk memasukkan nama vendor yang dapat mengatasi tantangan dan masalah tertentu seperti paket npm, alat open source atau bahkan produk komersial. Untuk menghindari daftar yang sangat panjang atau merekomendasikan proyek yang tidak bereputasi baik dan tidak stabil, kami membuat aturan berikut: + +- Hanya 3 vendor teratas yang disarankan - vendor yang muncul di 3 hasil teratas dari mesin pencari (Google atau GitHub diurutkan berdasarkan popularitas) untuk kata kunci relevan tertentu dapat dimasukkan dalam rekomendasi kami. +- Jika ini adalah paket npm, itu juga harus diunduh setidaknya rata-rata 750 kali sehari. +- Jika ini adalah proyek sumber terbuka, itu harus diperbarui setidaknya sekali dalam 6 bulan terakhir. \ No newline at end of file From 4ec7e27f19bb3d561f1e127e42a2f830d09ea30a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:04:14 +0000 Subject: [PATCH 0405/1795] docs: update README.md [skip ci] --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index af5b14af8..4be6c3176 100644 --- a/README.md +++ b/README.md @@ -1510,6 +1510,9 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Ryan Smith

    🖋
    Tom Boettger

    🖋 + +
    Joaquín Ormaechea

    🌍 + From ba817a0fe3672dfb1f9db01b1c229b1abe718dcf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:04:15 +0000 Subject: [PATCH 0406/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 76f817336..e1e994a09 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1014,6 +1014,15 @@ "contributions": [ "content" ] + }, + { + "login": "jormaechea", + "name": "Joaquín Ormaechea", + "avatar_url": "https://avatars3.githubusercontent.com/u/5612500?v=4", + "profile": "https://github.com/jormaechea", + "contributions": [ + "translation" + ] } ], "projectName": "nodebestpractices", From e3b8e9da80a5ed7837433c8609415f1daf5233cf Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:18:40 +0000 Subject: [PATCH 0407/1795] docs: update README.md [skip ci] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4be6c3176..d6e6c4c70 100644 --- a/README.md +++ b/README.md @@ -1512,6 +1512,7 @@ Thanks goes to these wonderful people who have contributed to this repository!
    Joaquín Ormaechea

    🌍 +
    dfrzuz

    🌍 From b4a779f0b96d7b2c02ed8d66aee8c23d60c18eb5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2020 10:18:41 +0000 Subject: [PATCH 0408/1795] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index e1e994a09..f53bc982d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1023,6 +1023,15 @@ "contributions": [ "translation" ] + }, + { + "login": "dfrzuz", + "name": "dfrzuz", + "avatar_url": "https://avatars3.githubusercontent.com/u/71859096?v=4", + "profile": "https://github.com/dfrzuz", + "contributions": [ + "translation" + ] } ], "projectName": "nodebestpractices", From 49f8ba2eb7f22e18c92e81ef56ef578b82b32d3e Mon Sep 17 00:00:00 2001 From: Yuki Ota Date: Sat, 3 Oct 2020 14:22:15 +0900 Subject: [PATCH 0409/1795] dupulicate japanese pages. resolve #2 --- .operations/writing-guidelines.japanese.md | 31 + README.japanese.md | 1521 +++++++++++++++++ .../eslint_prettier.japanese.md | 26 + .../avoid-build-time-secrets.japanese.md | 93 + .../docker/bootstrap-using-node.japanese.md | 85 + sections/docker/clean-cache.japanese.md | 27 + sections/docker/docker-ignore.japanese.md | 49 + sections/docker/generic-tips.japanese.md | 29 + sections/docker/graceful-shutdown.japanese.md | 84 + sections/docker/image-tags.japanese.md | 27 + .../docker/install-for-production.japanese.md | 86 + sections/docker/lint-dockerfile.japanese.md | 25 + sections/docker/memory-limit.japanese.md | 70 + .../docker/multi_stage_builds.japanese.md | 116 ++ ...estart-and-replicate-processes.japanese.md | 44 + sections/docker/scan-images.japanese.md | 30 + .../docker/smaller_base_images.japanese.md | 13 + ...e-cache-for-shorter-build-time.japanese.md | 115 ++ .../errorhandling/apmproducts.japanese.md | 28 + .../asyncerrorhandling.japanese.md | 109 ++ ...catchunhandledpromiserejection.japanese.md | 87 + .../centralizedhandling.japanese.md | 164 ++ .../documentingusingswagger.japanese.md | 52 + sections/errorhandling/failfast.japanese.md | 67 + sections/errorhandling/monitoring.japanese.md | 17 + .../operationalvsprogrammererror.japanese.md | 85 + .../shuttingtheprocess.japanese.md | 99 ++ .../testingerrorflows.japanese.md | 81 + .../errorhandling/usematurelogger.japanese.md | 50 + .../useonlythebuiltinerror.japanese.md | 117 ++ sections/performance/block-loop.japanese.md | 50 + .../performance/nativeoverutil.japanese.md | 69 + sections/production/LTSrelease.japanese.md | 20 + sections/production/apmproducts.japanese.md | 25 + .../assigntransactionid.japanese.md | 39 + sections/production/bestateless.japanese.md | 42 + .../createmaintenanceendpoint.japanese.md | 45 + .../production/delegatetoproxy.japanese.md | 51 + .../detectvulnerabilities.japanese.md | 20 + sections/production/frontendout.japanese.md | 45 + sections/production/guardprocess.japanese.md | 17 + .../installpackageswithnpmci.japanese.md | 30 + .../production/lockdependencies.japanese.md | 69 + sections/production/logrouting.japanese.md | 87 + sections/production/measurememory.japanese.md | 25 + sections/production/monitoring.japanese.md | 39 + .../production/productioncode.japanese.md | 16 + sections/production/setnodeenv.japanese.md | 34 + sections/production/smartlogging.japanese.md | 40 + sections/production/utilizecpu.japanese.md | 26 + .../breakintcomponents.japanese.md | 121 +- .../projectstructre/configguide.japanese.md | 43 + .../projectstructre/createlayers.japanese.md | 13 + .../separateexpress.japanese.md | 100 ++ .../thincomponents.japanese.md | 27 + .../projectstructre/wraputilities.japanese.md | 13 + .../avoid_publishing_secrets.japanese.md | 44 + sections/security/avoideval.japanese.md | 23 + sections/security/bcryptpasswords.japanese.md | 32 + sections/security/childprocesses.japanese.md | 31 + .../commonsecuritybestpractices.japanese.md | 131 ++ .../security/dependencysecurity.japanese.md | 53 + sections/security/escape-output.japanese.md | 62 + sections/security/expirejwt.japanese.md | 44 + sections/security/hideerrors.japanese.md | 25 + sections/security/limitrequests.japanese.md | 65 + sections/security/lintrules.japanese.md | 44 + .../security/login-rate-limit.japanese.md | 41 + sections/security/non-root-user.japanese.md | 41 + sections/security/ormodmusage.japanese.md | 54 + sections/security/regex.japanese.md | 34 + .../requestpayloadsizelimit.japanese.md | 60 + .../security/safemoduleloading.japanese.md | 15 + sections/security/saferedirects.japanese.md | 58 + sections/security/sandbox.japanese.md | 33 + .../security/secretmanagement.japanese.md | 36 + sections/security/secureheaders.japanese.md | 172 ++ sections/security/secureserver.japanese.md | 28 + sections/security/sessions.japanese.md | 40 + sections/security/validation.japanese.md | 68 + .../3-parts-in-name.japanese.md | 54 + sections/testingandquality/aaa.japanese.md | 61 + .../avoid-global-test-fixture.japanese.md | 42 + .../testingandquality/bumpversion.japanese.md | 27 + .../testingandquality/citools.japanese.md | 51 + .../testingandquality/refactoring.japanese.md | 43 + .../test-middlewares.japanese.md | 30 + 87 files changed, 5991 insertions(+), 84 deletions(-) create mode 100644 .operations/writing-guidelines.japanese.md create mode 100644 README.japanese.md create mode 100644 sections/codestylepractices/eslint_prettier.japanese.md create mode 100644 sections/docker/avoid-build-time-secrets.japanese.md create mode 100644 sections/docker/bootstrap-using-node.japanese.md create mode 100644 sections/docker/clean-cache.japanese.md create mode 100644 sections/docker/docker-ignore.japanese.md create mode 100644 sections/docker/generic-tips.japanese.md create mode 100644 sections/docker/graceful-shutdown.japanese.md create mode 100644 sections/docker/image-tags.japanese.md create mode 100644 sections/docker/install-for-production.japanese.md create mode 100644 sections/docker/lint-dockerfile.japanese.md create mode 100644 sections/docker/memory-limit.japanese.md create mode 100644 sections/docker/multi_stage_builds.japanese.md create mode 100644 sections/docker/restart-and-replicate-processes.japanese.md create mode 100644 sections/docker/scan-images.japanese.md create mode 100644 sections/docker/smaller_base_images.japanese.md create mode 100644 sections/docker/use-cache-for-shorter-build-time.japanese.md create mode 100644 sections/errorhandling/apmproducts.japanese.md create mode 100644 sections/errorhandling/asyncerrorhandling.japanese.md create mode 100644 sections/errorhandling/catchunhandledpromiserejection.japanese.md create mode 100644 sections/errorhandling/centralizedhandling.japanese.md create mode 100644 sections/errorhandling/documentingusingswagger.japanese.md create mode 100644 sections/errorhandling/failfast.japanese.md create mode 100644 sections/errorhandling/monitoring.japanese.md create mode 100644 sections/errorhandling/operationalvsprogrammererror.japanese.md create mode 100644 sections/errorhandling/shuttingtheprocess.japanese.md create mode 100644 sections/errorhandling/testingerrorflows.japanese.md create mode 100644 sections/errorhandling/usematurelogger.japanese.md create mode 100644 sections/errorhandling/useonlythebuiltinerror.japanese.md create mode 100644 sections/performance/block-loop.japanese.md create mode 100644 sections/performance/nativeoverutil.japanese.md create mode 100644 sections/production/LTSrelease.japanese.md create mode 100644 sections/production/apmproducts.japanese.md create mode 100644 sections/production/assigntransactionid.japanese.md create mode 100644 sections/production/bestateless.japanese.md create mode 100644 sections/production/createmaintenanceendpoint.japanese.md create mode 100644 sections/production/delegatetoproxy.japanese.md create mode 100644 sections/production/detectvulnerabilities.japanese.md create mode 100644 sections/production/frontendout.japanese.md create mode 100644 sections/production/guardprocess.japanese.md create mode 100644 sections/production/installpackageswithnpmci.japanese.md create mode 100644 sections/production/lockdependencies.japanese.md create mode 100644 sections/production/logrouting.japanese.md create mode 100644 sections/production/measurememory.japanese.md create mode 100644 sections/production/monitoring.japanese.md create mode 100644 sections/production/productioncode.japanese.md create mode 100644 sections/production/setnodeenv.japanese.md create mode 100644 sections/production/smartlogging.japanese.md create mode 100644 sections/production/utilizecpu.japanese.md create mode 100644 sections/projectstructre/configguide.japanese.md create mode 100644 sections/projectstructre/createlayers.japanese.md create mode 100644 sections/projectstructre/separateexpress.japanese.md create mode 100644 sections/projectstructre/thincomponents.japanese.md create mode 100644 sections/projectstructre/wraputilities.japanese.md create mode 100644 sections/security/avoid_publishing_secrets.japanese.md create mode 100644 sections/security/avoideval.japanese.md create mode 100644 sections/security/bcryptpasswords.japanese.md create mode 100644 sections/security/childprocesses.japanese.md create mode 100644 sections/security/commonsecuritybestpractices.japanese.md create mode 100644 sections/security/dependencysecurity.japanese.md create mode 100644 sections/security/escape-output.japanese.md create mode 100644 sections/security/expirejwt.japanese.md create mode 100644 sections/security/hideerrors.japanese.md create mode 100644 sections/security/limitrequests.japanese.md create mode 100644 sections/security/lintrules.japanese.md create mode 100644 sections/security/login-rate-limit.japanese.md create mode 100644 sections/security/non-root-user.japanese.md create mode 100644 sections/security/ormodmusage.japanese.md create mode 100644 sections/security/regex.japanese.md create mode 100644 sections/security/requestpayloadsizelimit.japanese.md create mode 100644 sections/security/safemoduleloading.japanese.md create mode 100644 sections/security/saferedirects.japanese.md create mode 100644 sections/security/sandbox.japanese.md create mode 100644 sections/security/secretmanagement.japanese.md create mode 100644 sections/security/secureheaders.japanese.md create mode 100644 sections/security/secureserver.japanese.md create mode 100644 sections/security/sessions.japanese.md create mode 100644 sections/security/validation.japanese.md create mode 100644 sections/testingandquality/3-parts-in-name.japanese.md create mode 100644 sections/testingandquality/aaa.japanese.md create mode 100644 sections/testingandquality/avoid-global-test-fixture.japanese.md create mode 100644 sections/testingandquality/bumpversion.japanese.md create mode 100644 sections/testingandquality/citools.japanese.md create mode 100644 sections/testingandquality/refactoring.japanese.md create mode 100644 sections/testingandquality/test-middlewares.japanese.md diff --git a/.operations/writing-guidelines.japanese.md b/.operations/writing-guidelines.japanese.md new file mode 100644 index 000000000..155b6bafa --- /dev/null +++ b/.operations/writing-guidelines.japanese.md @@ -0,0 +1,31 @@ +# Our content writing manifest + +How we enhance the reading and learning experience for our visitors. + +## 1. Simple is better than better + +Making it easy to read and absorb knowledge is our mission, we curate content. As such we focus on transforming complex and exhausting topics into a simplified list, trade overloaded information with shortened and less-accurate details, avoid ‘flammable’ and controversial topics and escape subjective ideas in favor of generally accepted practices. + +## 2. Be evidence-based and reliable + +Our readers should have great confidence that the content they skim through is reliable. We achieve this by including evidence like references, data and other resources available to this topic. Practically, strive to include quotes from reliable sources, show benchmarks, related design patterns or any scientific measure to prove your claims. + +## 3. MECE (Mutually Exclusive and Collectively Exhaustive) + +Apart from the content being greatly edited and reliable, skimming through it should also provide full coverage of the topic. No important sub-topic should be left out. + +## 4. Consistent formatting + +The content is presented using fixed templates. Any future content must conform to the same template. If you wish to add new bullets copy a bullet format from an existing bullet and extend it to your needs. For additional information please view [this template](https://github.com/goldbergyoni/nodebestpractices/blob/master/sections/template.md). + +## 5. It's About Node.js + +Each advice should be related directly to Node.js and not to software development in general. When we advise to implement generic pattern/rule in Node.js, the content should focus on the Node implementation. For example, when we advise to sanitize all requests input for security reasons, Node-lingo should be used - ‘Use middleware to sanitize request input’. If an item has no specific implementation in Node.js (e.g. it looks the same in Python & Jaba) - include it within a generic container item, see item 6.5 for example. + +## 6. Leading vendors only + +Sometimes it's useful to include names of vendors that can address certain challenges and problems like npm packages, open source tools or even commercial products. To avoid overwhelmingly long lists or recommending non-reputable and unstable projects, we came up with the following rules: + +- Only the top 3 vendors should be recommended – a vendor that appears in the top 3 results of a search engine (Google or GitHub sorted by popularity) for a given relevant keyword can be included in our recommendation. +- If it’s a npm package it must also be downloaded at least 750 times a day on average. +- If it’s an open-source project, it must have been updated at least once in the last 6 months. diff --git a/README.japanese.md b/README.japanese.md new file mode 100644 index 000000000..d6e6c4c70 --- /dev/null +++ b/README.japanese.md @@ -0,0 +1,1521 @@ +[✔]: assets/images/checkbox-small-blue.png + +# Node.js Best Practices + +

    + Node.js Best Practices +

    + +
    + +
    + 101 items Last update: March, 2020 Updated for Node 13.1.0 +
    + +
    + +[![nodepractices](/assets/images/twitter-s.png)](https://twitter.com/nodepractices/) **Follow us on Twitter!** [**@nodepractices**](https://twitter.com/nodepractices/) + + +
    + +Read in a different language: [![CN](/assets/flags/CN.png)**CN**](/README.chinese.md), [![BR](/assets/flags/BR.png)**BR**](/README.brazilian-portuguese.md), [![RU](/assets/flags/RU.png)**RU**](/README.russian.md), [![PL](/assets/flags/PL.png)**PL**](/README.polish.md) [(![ES](/assets/flags/ES.png)**ES**, ![FR](/assets/flags/FR.png)**FR**, ![HE](/assets/flags/HE.png)**HE**, ![KR](/assets/flags/KR.png)**KR** and ![TR](/assets/flags/TR.png)**TR** in progress!)](#translations) + +
    + +###### Built and maintained by our [Steering Committee](#steering-committee) and [Collaborators](#collaborators) + +# Latest Best Practices and News + +- **:tada: Node.js best practices reached 50k stars**: Thank you to each and every contributor who helped turning this project into what it is today! We've got lots of plans for the time ahead, as we expand our ever-growing list of Node.js best practices even further. + +- **🎧 Podcast**: Yoni Goldberg from our team participated in the last JS Party Podcast (Very cool one!) episode to speak about Node.js best practices, [🎧 listen here](https://changelog.com/jsparty/139) + +- **:whale: Node.js + Docker best practices**: We've just release the Docker with Node.js section which includes 15 bullets about better coding techqniues with Docker + +- **🎤 A talk at OdessaJS**: We will speak about Node.js testing this week at the great [OdessaJS conference](https://odessajs.org/) + +

    + +# Welcome! 3 Things You Ought To Know First + +**1. You are, in fact, reading dozens of the best Node.js articles -** this repository is a summary and curation of the top-ranked content on Node.js best practices, as well as content written here by collaborators + +**2. It is the largest compilation, and it is growing every week -** currently, more than 80 best practices, style guides, and architectural tips are presented. New issues and pull requests are created every day to keep this live book updated. We'd love to see you contributing here, whether that is fixing code mistakes, helping with translations, or suggesting brilliant new ideas. See our [writing guidelines here](/.operations/writing-guidelines.md) + +**3. Most best practices have additional info -** most bullets include a **🔗Read More** link that expands on the practice with code examples, quotes from selected blogs and more information + +

    + +## Table of Contents + +1. [Project Structure Practices (5)](#1-project-structure-practices) +2. [Error Handling Practices (11) ](#2-error-handling-practices) +3. [Code Style Practices (12) ](#3-code-style-practices) +4. [Testing And Overall Quality Practices (13) ](#4-testing-and-overall-quality-practices) +5. [Going To Production Practices (19) ](#5-going-to-production-practices) +6. [Security Practices (25)](#6-security-best-practices) +7. [Performance Practices (2) (Work In Progress️ ✍️)](#7-draft-performance-best-practices) +8. [Docker Practices (15)](#8-docker-best-practices) + +

    + +# `1. Project Structure Practices` + +## ![✔] 1.1 Structure your solution by components + +**TL;DR:** The worst large applications pitfall is maintaining a huge code base with hundreds of dependencies - such a monolith slows down developers as they try to incorporate new features. Instead, partition your code into components, each gets its own folder or a dedicated codebase, and ensure that each unit is kept small and simple. Visit 'Read More' below to see examples of correct project structure + +**Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependent components - deployments become slower and riskier. It's also considered harder to scale-out when all the business units are not separated + +🔗 [**Read More: structure by components**](/sections/projectstructre/breakintcomponents.md) + +

    + +## ![✔] 1.2 Layer your components, keep the web layer within its boundaries + +**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic, and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (e.g. Express req, res) to business logic and data layers - this makes your application dependent on and accessible only by specific web frameworks + +**Otherwise:** App that mixes web objects with other layers cannot be accessed by testing code, CRON jobs, triggers from message queues, etc + +🔗 [**Read More: layer your app**](/sections/projectstructre/createlayers.md) + +

    + +## ![✔] 1.3 Wrap common utilities as npm packages + +**TL;DR:** In a large app that constitutes a large code base, cross-cutting-concern utilities like logger, encryption and alike, should be wrapped by your own code and exposed as private npm packages. This allows sharing them among multiple code bases and projects + +**Otherwise:** You'll have to invent your own deployment and dependency wheel + +🔗 [**Read More: Structure by feature**](/sections/projectstructre/wraputilities.md) + +

    + +## ![✔] 1.4 Separate Express 'app' and 'server' + +**TL;DR:** Avoid the nasty habit of defining the entire [Express](https://expressjs.com/) app in a single huge file - separate your 'Express' definition to at least two files: the API declaration (app.js) and the networking concerns (WWW). For even better structure, locate your API declaration within components + +**Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file + +🔗 [**Read More: separate Express 'app' and 'server'**](/sections/projectstructre/separateexpress.md) + +

    + +## ![✔] 1.5 Use environment aware, secure and hierarchical config + +**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict). + +**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or devops team. Probably both + +🔗 [**Read More: configuration best practices**](/sections/projectstructre/configguide.md) + +


    + +

    ⬆ Return to top

    + +# `2. Error Handling Practices` + +## ![✔] 2.1 Use Async-Await or promises for async error handling + +**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using a reputable promise library or async-await instead which enables a much more compact and familiar code syntax like try-catch + +**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns + +🔗 [**Read More: avoiding callbacks**](/sections/errorhandling/asyncerrorhandling.md) + +

    + +## ![✔] 2.2 Use only the built-in Error object + +**TL;DR:** Many throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Whether you reject a promise, throw an exception or emit an error – using only the built-in Error object (or an object that extends the built-in Error object) will increase uniformity and prevent loss of information + +**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! + +🔗 [**Read More: using the built-in error object**](/sections/errorhandling/useonlythebuiltinerror.md) + +

    + +## ![✔] 2.3 Distinguish operational vs programmer errors + +**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, programmer error (e.g. trying to read undefined variable) refers to unknown code failures that dictate to gracefully restart the application + +**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? the opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context + +🔗 [**Read More: operational vs programmer error**](/sections/errorhandling/operationalvsprogrammererror.md) + +

    + +## ![✔] 2.4 Handle errors centrally, not within a middleware + +**TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in + +**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors + +🔗 [**Read More: handling errors in a centralized place**](/sections/errorhandling/centralizedhandling.md) + +

    + +## ![✔] 2.5 Document API errors using Swagger or GraphQL + +**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like Swagger. If you're using GraphQL, you can utilize your schema and comments as well. + +**Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) + +🔗 [**Read More: documenting API errors in Swagger or GraphQL**](/sections/errorhandling/documentingusingswagger.md) + +

    + +## ![✔] 2.6 Exit the process gracefully when a stranger comes to town + +**TL;DR:** When an unknown error occurs (a developer error, see best practice 2.3) - there is uncertainty about the application healthiness. A common practice suggests restarting the process carefully using a process management tool like [Forever](https://www.npmjs.com/package/forever) or [PM2](http://pm2.keymetrics.io/) + +**Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily + +🔗 [**Read More: shutting the process**](/sections/errorhandling/shuttingtheprocess.md) + +

    + +## ![✔] 2.7 Use a mature logger to increase error visibility + +**TL;DR:** A set of mature logging tools like [Pino](https://github.com/pinojs/pino) or [Log4js](https://www.npmjs.com/package/log4js), will speed-up error discovery and understanding. So forget about console.log + +**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late + +🔗 [**Read More: using a mature logger**](/sections/errorhandling/usematurelogger.md) + +

    + +## ![✔] 2.8 Test error flows using your favorite test framework + +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. Testing frameworks like Mocha & Chai can handle this easily (see code examples within the "Gist popup") + +**Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling + +🔗 [**Read More: testing error flows**](/sections/errorhandling/testingerrorflows.md) + +

    + +## ![✔] 2.9 Discover errors and downtime using APM products + +**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes and slow parts that you were missing + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: using APM products**](/sections/errorhandling/apmproducts.md) + +

    + +## ![✔] 2.10 Catch unhandled promise rejections + +**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` + +**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about + +🔗 [**Read More: catching unhandled promise rejection**](/sections/errorhandling/catchunhandledpromiserejection.md) + +

    + +## ![✔] 2.11 Fail fast, validate arguments using a dedicated library + +**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like [ajv](https://www.npmjs.com/package/ajv) and [Joi](https://www.npmjs.com/package/joi) + +**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? + +🔗 [**Read More: failing fast**](/sections/errorhandling/failfast.md) + +


    + +

    ⬆ Return to top

    + +# `3. Code Style Practices` + +## ![✔] 3.1 Use ESLint + +**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint + +**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style + +🔗 [**Read More: Using ESLint and Prettier**](/sections/codestylepractices/eslint_prettier.md) + +

    + +## ![✔] 3.2 Node.js specific plugins + +**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) + +**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early + +

    + +## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line + +**TL;DR:** The opening curly braces of a code block should be on the same line as the opening statement + +### Code Example + +```javascript +// Do +function someFunction() { + // code block +} + +// Avoid +function someFunction() +{ + // code block +} +``` + +**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: + +🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) + +

    + +## ![✔] 3.4 Separate your statements properly + +No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. + +**TL;DR:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. + +**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediate invoked function expressions to prevent most of unexpected errors. + +### Code example + +```javascript +// Do +function doThing() { + // ... +} + +doThing() + +// Do + +const items = [1, 2, 3] +items.forEach(console.log) + +// Avoid — throws exception +const m = new Map() +const a = [1,2,3] +[...m.values()].forEach(console.log) +> [...m.values()].forEach(console.log) +> ^^^ +> SyntaxError: Unexpected token ... + +// Avoid — throws exception +const count = 2 // it tries to run 2(), but 2 is not a function +(function doSomething() { + // do something amazing +}()) +// put a semicolon before the immediate invoked function, after the const definition, save the return value of the anonymous function to a variable or avoid IIFEs alltogether +``` + +🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) +🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline) + +

    + +## ![✔] 3.5 Name your functions + +**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot + +**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions + +

    + +## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes + +**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions and **_UpperCamelCase_** (capital first letter as well) when naming classes. This will help you to easily distinguish between plain variables/functions, and classes that require instantiation. Use descriptive names, but try to keep them short + +**Otherwise:** Javascript is the only language in the world which allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase + +### 3.6 Code Example + +```javascript +// for class name we use UpperCamelCase +class SomeClassExample {} + +// for const names we use the const keyword and lowerCamelCase +const config = { + key: "value" +}; + +// for variables and functions names we use lowerCamelCase +let someVariableExample = "value"; +function doSomething() {} +``` + +

    + +## ![✔] 3.7 Prefer const over let. Ditch the var + +**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal + +**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes + +🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) + +

    + +## ![✔] 3.8 Require modules first, not inside functions + +**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems + +**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its own dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function + +

    + +## ![✔] 3.9 Require modules by folders, opposed to the files directly + +**TL;DR:** When developing a module/library in a folder, place an index.js file that exposes the module's internals so every consumer will pass through it. This serves as an 'interface' to your module and eases future changes without breaking the contract + +**Otherwise:** Changing the internal structure of files or the signature may break the interface with clients + +### 3.9 Code example + +```javascript +// Do +module.exports.SMSProvider = require("./SMSProvider"); +module.exports.SMSNumberResolver = require("./SMSNumberResolver"); + +// Avoid +module.exports.SMSProvider = require("./SMSProvider/SMSProvider.js"); +module.exports.SMSNumberResolver = require("./SMSNumberResolver/SMSNumberResolver.js"); +``` + +

    + +## ![✔] 3.10 Use the `===` operator + +**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal + +**Otherwise:** Unequal variables might return true when compared with the `==` operator + +### 3.10 Code example + +```javascript +"" == "0"; // false +0 == ""; // true +0 == "0"; // true + +false == "false"; // false +false == "0"; // true + +false == undefined; // false +false == null; // false +null == undefined; // true + +" \t\r\n " == 0; // true +``` + +All statements above will return false if used with `===` + +

    + +## ![✔] 3.11 Use Async Await, avoid callbacks + +**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch + +**Otherwise:** Handling async errors in callback style is probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting and makes it difficult to reason about the code flow + +🔗[**Read more:** Guide to async await 1.0](https://github.com/yortus/asyncawait) + +

    + +## ![✔] 3.12 Use arrow function expressions (=>) + +**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) + +**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read + +🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) + +


    + +

    ⬆ Return to top

    + +# `4. Testing And Overall Quality Practices` + +## ![✔] 4.1 At the very least, write API (component) testing + +**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/). Afterward, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc + +**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage + +

    + +## ![✔] 4.2 Include 3 parts in each test name + +**TL;DR:** Make the test speak at the requirements level so it's self explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances and what is the expected result + +**Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? + +🔗 [**Read More: Include 3 parts in each test name**](/sections/testingandquality/3-parts-in-name.md) + +

    + +## ![✔] 4.3 Structure tests by the AAA pattern + +**TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan + +**Otherwise:** Not only you spend long daily hours on understanding the main code, now also what should have been the simple part of the day (testing) stretches your brain + +🔗 [**Read More: Structure tests by the AAA pattern**](/sections/testingandquality/aaa.md) + +

    + +## ![✔] 4.4 Detect code issues with a linter + +**TL;DR:** Use a code linter to check basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](#3-code-style-practices) on Code Style Practices + +**Otherwise:** You may let pass some anti-pattern and possible vulnerable code to your production environment. + +

    + +## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test + +**TL;DR:** To prevent tests coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records + +**Otherwise:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build + +🔗 [**Read More: Avoid global test fixtures**](/sections/testingandquality/avoid-global-test-fixture.md) + +

    + +## ![✔] 4.6 Constantly inspect for vulnerable dependencies + +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [npm audit](https://docs.npmjs.com/cli/audit) and 🔗 [snyk.io](https://snyk.io) that can be invoked from your CI on every build + +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious + +

    + +## ![✔] 4.7 Tag your tests + +**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' + +**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests + +

    + +## ![✔] 4.8 Check your test coverage, it helps to identify wrong test patterns + +**TL;DR:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold + +**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing + +

    + +## ![✔] 4.9 Inspect for outdated packages + +**TL;DR:** Use your preferred tool (e.g. 'npm outdated' or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates) to detect installed packages which are outdated, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version + +**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky + +

    + +## ![✔] 4.10 Use production-like environment for e2e testing + +**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) + +**Otherwise:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments + +

    + +## ![✔] 4.11 Refactor regularly using static analysis tools + +**TL;DR:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity) and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). + +**Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix + +🔗 [**Read More: Refactoring!**](/sections/testingandquality/refactoring.md) + +

    + +## ![✔] 4.12 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) + +**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of complex setup that demands a steep learning curve. Nowadays, it has become much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully + +**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup + +🔗 [**Read More: Choosing CI platform**](/sections/testingandquality/citools.md) + +## ![✔] 4.13 Test your middlewares in isolation + +**TL;DR:** When a middleware holds some immense logic that spans many request, it worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects + +**Otherwise:** A bug in Express middleware === a bug in all or most requests + +🔗 [**Read More: Test middlewares in isolation**](/sections/testingandquality/test-middlewares.md) + +


    + +

    ⬆ Return to top

    + +# `5. Going To Production Practices` + +## ![✔] 5.1. Monitoring + +**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions + +**Otherwise:** Failure === disappointed customers. Simple + +🔗 [**Read More: Monitoring!**](/sections/production/monitoring.md) + +

    + +## ![✔] 5.2. Increase transparency using smart logging + +**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted + +**Otherwise:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information + +🔗 [**Read More: Increase transparency using smart logging**](/sections/production/smartlogging.md) + +

    + +## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy + +**TL;DR:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead + +**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly + +🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](/sections/production/delegatetoproxy.md) + +

    + +## ![✔] 5.4. Lock dependencies + +**TL;DR:** Your code must be identical across all environments, but amazingly npm lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using npm config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grained control use `npm shrinkwrap`. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default + +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code + +🔗 [**Read More: Lock dependencies**](/sections/production/lockdependencies.md) + +

    + +## ![✔] 5.5. Guard process uptime using the right tool + +**TL;DR:** The process must go on and get restarted upon failures. For simple scenarios, process management tools like PM2 might be enough but in today's ‘dockerized’ world, cluster management tools should be considered as well + +**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos + +🔗 [**Read More: Guard process uptime using the right tool**](/sections/production/guardprocess.md) + +

    + +## ![✔] 5.6. Utilize all CPU cores + +**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) + +**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) + +🔗 [**Read More: Utilize all CPU cores**](/sections/production/utilizecpu.md) + +

    + +## ![✔] 5.7. Create a ‘maintenance endpoint’ + +**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tests tools, some valuable information and operations are easier done using code + +**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes + +🔗 [**Read More: Create a ‘maintenance endpoint’**](/sections/production/createmaintenanceendpoint.md) + +

    + +## ![✔] 5.8. Discover errors and downtime using APM products + +**TL;DR:** Application monitoring and performance products (a.k.a APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-users side while suggesting the root cause + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: Discover errors and downtime using APM products**](/sections/production/apmproducts.md) + +

    + +## ![✔] 5.9. Make your code production-ready + +**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) + +**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written + +🔗 [**Read More: Make your code production-ready**](/sections/production/productioncode.md) + +

    + +## ![✔] 5.10. Measure and guard the memory usage + +**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system + +**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) + +🔗 [**Read More: Measure and guard the memory usage**](/sections/production/measurememory.md) + +

    + +## ![✔] 5.11. Get your frontend assets out of Node + +**TL;DR:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single-threaded model + +**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content + +🔗 [**Read More: Get your frontend assets out of Node**](/sections/production/frontendout.md) + +

    + +## ![✔] 5.12. Be stateless, kill your servers almost every day + +**TL;DR:** Store any type of data (e.g. user sessions, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior + +**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server + +🔗 [**Read More: Be stateless, kill your Servers almost every day**](/sections/production/bestateless.md) + +

    + +## ![✔] 5.13. Use tools that automatically detect vulnerabilities + +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately + +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious + +🔗 [**Read More: Use tools that automatically detect vulnerabilities**](/sections/production/detectvulnerabilities.md) + +

    + +## ![✔] 5.14. Assign a transaction id to each log statement + +**TL;DR:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Unfortunately, this is not easy to achieve in Node due to its async nature, see code examples inside + +**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue + +🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](/sections/production/assigntransactionid.md) + +

    + +## ![✔] 5.15. Set NODE_ENV=production + +**TL;DR:** Set the environment variable NODE_ENV to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many npm packages determine the current environment and optimize their code for production + +**Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! + +🔗 [**Read More: Set NODE_ENV=production**](/sections/production/setnodeenv.md) + +

    + +## ![✔] 5.16. Design automated, atomic and zero-downtime deployments + +**TL;DR:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment + +**Otherwise:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features + +

    + +## ![✔] 5.17. Use an LTS release of Node.js + +**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements + +**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain + +🔗 [**Read More: Use an LTS release of Node.js**](/sections/production/LTSrelease.md) + +

    + +## ![✔] 5.18. Don't route logs within the app + +**TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). + +**Otherwise:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns + +🔗 [**Read More: Log Routing**](/sections/production/logrouting.md) + +

    + +## ![✔] 5.19. Install your packages with `npm ci` + +**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Using this command is recommended in automated environments such as continuous integration pipelines. + +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. + +🔗 [**Read More: Use npm ci**](/sections/production/installpackageswithnpmci.md) + +


    + +

    ⬆ Return to top

    + +# `6. Security Best Practices` + +
    +54 items +
    + +## ![✔] 6.1. Embrace linter security rules + + + +**TL;DR:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter + +**Otherwise:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories + +🔗 [**Read More: Lint rules**](/sections/security/lintrules.md) + +

    + +## ![✔] 6.2. Limit concurrent requests using a middleware + + + +**TL;DR:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) + +**Otherwise:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. + +🔗 [**Read More: Implement rate limiting**](/sections/security/limitrequests.md) + +

    + +## ![✔] 6.3 Extract secrets from config files or use packages to encrypt them + + + +**TL;DR:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally + +**Otherwise:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). + +🔗 [**Read More: Secret management**](/sections/security/secretmanagement.md) + +

    + +## ![✔] 6.4. Prevent query injection vulnerabilities with ORM/ODM libraries + + + +**TL;DR:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. + +**Otherwise:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. + +🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](/sections/security/ormodmusage.md) + +

    + +## ![✔] 6.5. Collection of generic security best practices + +**TL;DR:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Common security best practices**](/sections/security/commonsecuritybestpractices.md) + +

    + +## ![✔] 6.6. Adjust the HTTP response headers for enhanced security + + + +**TL;DR:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). + +**Otherwise:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities + +🔗 [**Read More: Using secure headers in your application**](/sections/security/secureheaders.md) + +

    + +## ![✔] 6.7. Constantly and automatically inspect for vulnerable dependencies + + + +**TL;DR:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. + +**Otherwise:** An attacker could detect your web framework and attack all its known vulnerabilities. + +🔗 [**Read More: Dependency security**](/sections/security/dependencysecurity.md) + +

    + +## ![✔] 6.8. Avoid using the Node.js crypto library for handling passwords, use Bcrypt + + + +**TL;DR:** Passwords or secrets (API keys) should be stored using a secure hash + salt function like `bcrypt`, that should be a preferred choice over its JavaScript implementation due to performance and security reasons. + +**Otherwise:** Passwords or secrets that are persisted without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. + +🔗 [**Read More: Use Bcrypt**](/sections/security/bcryptpasswords.md) + +

    + +## ![✔] 6.9. Escape HTML, JS and CSS output + + + +**TL;DR:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) + +**Otherwise:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients + +🔗 [**Read More: Escape output**](/sections/security/escape-output.md) + +

    + +## ![✔] 6.10. Validate incoming JSON schemas + + + +**TL;DR:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) + +**Otherwise:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application + +🔗 [**Read More: Validate incoming JSON schemas**](/sections/security/validation.md) + +

    + +## ![✔] 6.11. Support blacklisting JWTs + + + +**TL;DR:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blacklist of untrusted tokens that are validated on each request. + +**Otherwise:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. + +🔗 [**Read More: Blacklist JSON Web Tokens**](/sections/security/expirejwt.md) + +

    + +## ![✔] 6.12. Prevent brute-force attacks against authorization + + + +**TL;DR:** A simple and powerful technique is to limit authorization attempts using two metrics: + +1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. +2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. + +**Otherwise:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application + +🔗 [**Read More: Login rate limiting**](/sections/security/login-rate-limit.md) + +

    + +## ![✔] 6.13. Run Node.js as non-root user + + + +**TL;DR:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" + +**Otherwise:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to his server) + +🔗 [**Read More: Run Node.js as non-root user**](/sections/security/non-root-user.md) + +

    + +## ![✔] 6.14. Limit payload size using a reverse-proxy or a middleware + + + +**TL;DR:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads + +**Otherwise:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks + +🔗 [**Read More: Limit payload size**](/sections/security/requestpayloadsizelimit.md) + +

    + +## ![✔] 6.15. Avoid JavaScript eval statements + + + +**TL;DR:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. + +**Otherwise:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. + +🔗 [**Read More: Avoid JavaScript eval statements**](/sections/security/avoideval.md) + +

    + +## ![✔] 6.16. Prevent evil RegEx from overloading your single thread execution + + + +**TL;DR:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns + +**Otherwise:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 + +🔗 [**Read More: Prevent malicious RegEx**](/sections/security/regex.md) + +

    + +## ![✔] 6.17. Avoid module loading using a variable + + + +**TL;DR:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough + +**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. + +🔗 [**Read More: Safe module loading**](/sections/security/safemoduleloading.md) + +

    + +## ![✔] 6.18. Run unsafe code in a sandbox + + + +**TL;DR:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox + +**Otherwise:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables + +🔗 [**Read More: Run unsafe code in a sandbox**](/sections/security/sandbox.md) + +

    + +## ![✔] 6.19. Take extra care when working with child processes + + + +**TL;DR:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. + +**Otherwise:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. + +🔗 [**Read More: Be cautious when working with child processes**](/sections/security/childprocesses.md) + +

    + +## ![✔] 6.20. Hide error details from clients + + + +**TL;DR:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details + +**Otherwise:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace + +🔗 [**Read More: Hide error details from client**](/sections/security/hideerrors.md) + +

    + +## ![✔] 6.21. Configure 2FA for npm or Yarn + + + +**TL;DR:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. + +**Otherwise:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) + +

    + +## ![✔] 6.22. Modify session middleware settings + + + +**TL;DR:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) + +**Otherwise:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities + +🔗 [**Read More: Cookie and session security**](/sections/security/sessions.md) + +

    + +## ![✔] 6.23. Avoid DOS attacks by explicitly setting when a process should crash + + + +**TL;DR:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) + +**Otherwise:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease + +

    + +## ![✔] 6.24. Prevent unsafe redirects + + + +**TL;DR:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. + +**Otherwise:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. + +🔗 [**Read More: Prevent unsafe redirects**](/sections/security/saferedirects.md) + +

    + +## ![✔] 6.25. Avoid publishing secrets to the npm registry + + + +**TL;DR:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. + +**Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. + +🔗 [**Read More: Avoid publishing secrets**](/sections/security/avoid_publishing_secrets.md) +


    + +

    ⬆ Return to top

    + +# `7. Draft: Performance Best Practices` + +## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/256) + +

    + +## ![✔] 7.1. Don't block the event loop + +**TL;DR:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. + +**Otherwise:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** + +🔗 [**Read More: Do not block the event loop**](/sections/performance/block-loop.md) + +


    + +## ![✔] 7.2. Prefer native JS methods over user-land utils like Lodash + +**TL;DR:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. +Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. + +**Otherwise:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. + +🔗 [**Read More: Native over user land utils**](/sections/performance/nativeoverutil.md) + +


    + +

    ⬆ Return to top

    + +# `8. Docker Best Practices` + +🏅 Many thanks to [Bret Fisher](https://github.com/BretFisher) from whom we learned many of the following practices + +

    + +## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images + +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. + +**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. + +### Example Dockerfile for multi-stage builds + +```dockerfile +FROM node:14.4.0 AS build + +COPY . . +RUN npm ci && npm run build + +FROM node:slim-14.4.0 + +USER node +EXPOSE 8080 + +COPY --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/package-lock.json ./ +RUN npm ci --production + +CMD [ "node", "dist/app.js" ] +``` + +🔗 [**Read More: Use multi-stage builds**](/sections/docker/multi_stage_builds.md) + +


    + +## ![✔] 8.2. Bootstrap using 'node' command, avoid npm start + +**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-process, signal handling, graceful shutdown and having zombie processes. + +**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data. + +[**Read More: Bootstrap container using node command, avoid npm start**](/sections/docker/bootstrap-using-node.md) + +


    + +## ![✔] 8.3. Let the Docker runtime handle replication and uptime + +**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes + +**Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance + +🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](/sections/docker/restart-and-replicate-processes.md) + +


    + +## ![✔] 8.4. Use .dockerignore to prevent leaking secrets + +**TL;DR**: Include a `.dockerignore` file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker + +**Otherwise**: Common personal secret files like `.env`, `.aws` and `.npmrc` will be shared with anybody with access to the image (e.g. Docker repository) + +🔗 [**Read More: Use .dockerignore**](/sections/docker/docker-ignore.md) + +


    + +## ![✔] 8.5. Clean-up dependencies before production + +**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` + +**Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) + +🔗 Read More: [Remove development dependencies](/sections/docker/install-for-production.md) + +


    + +## ![✔] 8.6. Shutdown smartly and gracefully + +**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources + +**Otherwise:** Dying immediately means not responding to thousands of disappointed users + +🔗 [**Read More: Graceful shutdown**](/sections/docker/graceful-shutdown.md) + +


    + +## ![✔] 8.7. Set memory limits using both Docker and v8 + +**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit + +**Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources + +🔗 [**Read More: Set memory limits using Docker only**](/sections/docker/memory-limit.md) + +


    + +## ![✔] 8.8. Plan for efficient caching + +**TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. + +**Otherwise:** Docker build will be very long and consume lot of resources even when making tiny changes + +🔗 [**Read More: Leverage caching to reduce build times**](/sections/docker/use-cache-for-shorter-build-time.md) + +


    + +## ![✔] 8.9. Use explicit image reference, avoid `latest` tag + +**TL;DR:** Specify an explicit image digest or versioned label, never refer to `latest`. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. + +In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. + +**Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. + +🔗 [**Read More: Understand image tags and use the "latest" tag with caution**](/sections/docker/image-tags.md) + +


    + +## ![✔] 8.10. Prefer smaller Docker base images + +**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. + +**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. + +🔗 [**Read More: Prefer smaller images**](/sections/docker/smaller_base_images.md) + +


    + +## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args + +**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces + +**Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus + +🔗 [**Read More: Clean-out build-time secrets**](/sections/docker/avoid-build-time-secrets.md) + +


    + +## ![✔] 8.12. Scan images for multi layers of vulnerabilities + +**TL;DR:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins + +**Otherwise:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications + +🔗 [**Read More: Generic Docker practices**](/sections/docker/scan-images.md) + +


    + +## ![✔] 8.13 Clean NODE_MODULE cache + +**TL;DR:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off + +**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used + +🔗 [**Read More: Clean NODE_MODULE cache**](/sections/docker/clean-cache.md) + +


    + +## ![✔] 8.14. Generic Docker practices + +**TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Generic Docker practices**](/sections/docker/generic-tips.md) + +


    + + +## ![✔] 8.15. Lint your Dockerfile + +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. + +**Otherwise:** Mistakenely the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. + +🔗 [**Read More: Lint your Dockerfile**](/sections/docker/lint-dockerfile.md) + +


    + +

    ⬆ Return to top

    + +# Milestones + +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/goldbergyoni/nodebestpractices/milestones) and join the working groups if you want to contribute to this project + +
    + +## Translations + +All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! + +### Completed translations + +- ![BR](/assets/flags/BR.png) [Brazilian Portuguese](./README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) +- ![CN](/assets/flags/CN.png) [Chinese](./README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) +- ![RU](/assets/flags/RU.png) [Russian](./README.russian.md) - Courtesy of [Alex Ivanov](https://github.com/contributorpw) +- ![PL](/assets/flags/PL.png) [Polish](./README.polish.md) - Courtesy of [Michal Biesiada](https://github.com/mbiesiad) + +### Translations in progress + +- ![FR](/assets/flags/FR.png) [French](https://github.com/gaspaonrocks/nodebestpractices/blob/french-translation/README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](/assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](/assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![ES](/assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](/assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139)) + +

    + +## Steering Committee + +Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [Github projects](https://github.com/goldbergyoni/nodebestpractices/projects). + + + +[Yoni Goldberg](https://github.com/goldbergyoni) + + + +Independent Node.js consultant who works with customers in the USA, Europe, and Israel on building large-scale Node.js applications. Many of the best practices above were first published at [goldbergyoni.com](https://goldbergyoni.com). Reach Yoni at [@goldbergyoni](https://github.com/goldbergyoni) or [me@goldbergyoni.com](mailto:me@goldbergyoni.com) + +
    + + + +[Bruno Scheufler](https://github.com/BrunoScheufler) + + +💻 full-stack web engineer, Node.js & GraphQL enthusiast + +
    + + + +[Kyle Martin](https://github.com/js-kyle) + + + +Full Stack Developer & Site Reliability Engineer based in New Zealand, interested in web application security, and architecting and building Node.js applications to perform at global scale. + +
    + + + +[Kevyn Bruyere](https://github.com/kevynb) + + +Independent full-stack developer with a taste for Ops and automation. + +
    + +### Steering Committee Emeriti + + + +[Sagir Khan](https://github.com/sagirk) + + + + +Deep specialist in JavaScript and its ecosystem — React, Node.js, TypeScript, GraphQL, MongoDB, pretty much anything that involves JS/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation. + +
    + +## Collaborators + +Thank you to all our collaborators! 🙏 + +Our collaborators are members who are contributing to the repository on a regular basis, through suggesting new best practices, triaging issues, reviewing pull requests and more. If you are interested in helping us guide thousands of people to craft better Node.js applications, please read our [contributor guidelines](/.operations/CONTRIBUTING.md) 🎉 + +| | | +| :---------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | +| [Ido Richter (Founder)](https://github.com/idori) | [Keith Holliday](https://github.com/TheHollidayInn) | + +### Collaborator Emeriti + +| | +| :-------------------------------------------------------------------------------------------------------------------------: | +| [Refael Ackermann](https://github.com/refack) | + +
    + +## Contributing +If you've ever wanted to contribute to open source, now is your chance! See the [contributing docs](.operations/CONTRIBUTING.md) for more information. + +## Contributors ✨ + +Thanks goes to these wonderful people who have contributed to this repository! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Kevin Rambaud

    🖋

    Michael Fine

    🖋

    Shreya Dahal

    🖋

    Matheus Cruz Rocha

    🖋

    Yog Mehta

    🖋

    Kudakwashe Paradzayi

    🖋

    t1st3

    🖋

    mulijordan1976

    🖋

    Matan Kushner

    🖋

    Fabio Hiroki

    🖋

    James Sumners

    🖋

    Dan Gamble

    🖋

    PJ Trainor

    🖋

    Remek Ambroziak

    🖋

    Yoni Jah

    🖋

    Misha Khokhlov

    🖋

    Evgeny Orekhov

    🖋

    -

    🖋

    Isaac Halvorson

    🖋

    Vedran Karačić

    🖋

    lallenlowe

    🖋

    Nathan Wells

    🖋

    Paulo Reis

    🖋

    syzer

    🖋

    David Sancho

    🖋

    Robert Manolea

    🖋

    Xavier Ho

    🖋

    Aaron

    🖋

    Jan Charles Maghirang Adona

    🖋

    Allen

    🖋

    Leonardo Villela

    🖋

    Michał Załęcki

    🖋

    Chris Nicola

    🖋

    Alejandro Corredor

    🖋

    cwar

    🖋

    Yuwei

    🖋

    Utkarsh Bhatt

    🖋

    Duarte Mendes

    🖋

    Jason Kim

    🖋

    Mitja O.

    🖋

    Sandro Miguel Marques

    🖋

    Gabe

    🖋

    Ron Gross

    🖋

    Valeri Karpov

    🖋

    Sergio Bernal

    🖋

    Nikola Telkedzhiev

    🖋

    Vitor Godoy

    🖋

    Manish Saraan

    🖋

    Sangbeom Han

    🖋

    blackmatch

    🖋

    Joe Reeve

    🖋

    Ryan Busby

    🖋

    Iman Mohamadi

    🖋

    Sergii Paryzhskyi

    🖋

    Kapil Patel

    🖋

    迷渡

    🖋

    Hozefa

    🖋

    Ethan

    🖋

    Sam

    🖋

    Arlind

    🖋

    Teddy Toussaint

    🖋

    Lewis

    🖋

    Gabriel Lidenor

    🖋

    Roman

    🖋

    Francozeira

    🖋

    Invvard

    🖋

    Rômulo Garofalo

    🖋

    Tho Q Luong

    🖋

    Burak Shen

    🖋

    Martin Muzatko

    🖋

    Jared Collier

    🖋

    Hilton Meyer

    🖋

    ChangJoo Park(박창주)

    🖋

    Masahiro Sakaguchi

    🖋

    Keith Holliday

    🖋

    coreyc

    🖋

    Maximilian Berkmann

    🖋

    Douglas Mariano Valero

    🖋

    Marcelo Melo

    🖋

    Mehmet Perk

    🖋

    ryan ouyang

    🖋

    Shabeer

    🖋

    Eduard Kyvenko

    🖋

    Deyvison Rocha

    🖋

    George Mamer

    🖋

    Konstantinos Leimonis

    🖋

    Oliver Lluberes

    🌍

    Tien Do

    🖋

    Ranvir Singh

    🖋

    Vadim Nicolaev

    🖋

    German Gamboa Gonzalez

    🖋

    Hafez

    🖋

    Chandiran

    🖋

    VinayaSathyanarayana

    🖋

    Kim Kern

    🖋

    Kenneth Freitas

    🖋

    songe

    🖋

    Kirill Shekhovtsov

    🖋

    Serge

    🖋

    keyrwinz

    🖋

    Dmitry Nikitenko

    🖋

    bushuai

    👀 🖋

    Benjamin Gruenbaum

    🖋

    Ezequiel

    🌍

    Juan José Rodríguez

    🌍

    Or Bin

    🖋

    Andreo Vieira

    🖋

    Michael Solomon

    🖋

    Jimmy Callin

    🖋

    Siddharth

    🖋

    Ryan Smith

    🖋

    Tom Boettger

    🖋

    Joaquín Ormaechea

    🌍

    dfrzuz

    🌍
    + + + + diff --git a/sections/codestylepractices/eslint_prettier.japanese.md b/sections/codestylepractices/eslint_prettier.japanese.md new file mode 100644 index 000000000..8d52a1dbd --- /dev/null +++ b/sections/codestylepractices/eslint_prettier.japanese.md @@ -0,0 +1,26 @@ +# Using ESLint and Prettier + + +### Comparing ESLint and Prettier + +If you format this code using ESLint, it will just give you a warning that it's too wide (depends on your `max-len` setting). Prettier will automatically format it for you. + +```javascript +foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne(), noWayYouGottaBeKiddingMe()); +``` + +```javascript +foo( + reallyLongArg(), + omgSoManyParameters(), + IShouldRefactorThis(), + isThereSeriouslyAnotherOne(), + noWayYouGottaBeKiddingMe() +); +``` + +Source: [https://github.com/prettier/prettier-eslint/issues/101](https://github.com/prettier/prettier-eslint/issues/101) + +### Integrating ESLint and Prettier + +ESLint and Prettier overlap in the code formatting feature but can be easily combined by using other packages like [prettier-eslint](https://github.com/prettier/prettier-eslint), [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), and [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier). For more information about their differences, you can view the link [here](https://stackoverflow.com/questions/44690308/whats-the-difference-between-prettier-eslint-eslint-plugin-prettier-and-eslint). diff --git a/sections/docker/avoid-build-time-secrets.japanese.md b/sections/docker/avoid-build-time-secrets.japanese.md new file mode 100644 index 000000000..2d33ee088 --- /dev/null +++ b/sections/docker/avoid-build-time-secrets.japanese.md @@ -0,0 +1,93 @@ +# Clean build-time secrets, avoid secrets as args + +

    + +### One Paragraph Explainer + + +A Docker image isn't just a bunch of files but rather multiple layers revealing what happened during build-time. In a very common scenario, developers need the npm token during build time (mostly for private registries) - this is falsely achieved by passing the token as a build time args. It might seem innocent and safe, however this token can now be fetched from the developer's machine Docker history, from the Docker registry and the CI. An attacker who gets access to that token is now capable of writing into the organization private npm registry. There are two more secured alternatives: The flawless one is using Docker --secret feature (experimental as of July 2020) which allows mounting a file during build time only. The second approach is using multi-stage build with args, building and then copying only the necessary files to production. The last technique will not ship the secrets with the images but will appear in the local Docker history - This is typically considered as secured enough for most organizations. + +

    + +### Code Example – Using Docker mounted secrets (experimental but stable) + +
    + +Dockerfile + +``` +# syntax = docker/dockerfile:1.0-experimental + +FROM node:12-slim +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN --mount=type=secret,id=npm,target=/root/.npmrc npm ci + +# The rest comes here +``` + +
    + +

    + +### Code Example – Building securely using multi-stage build + +
    + +Dockerfile + +``` + +FROM node:12-slim AS build +ARG NPM_TOKEN +WORKDIR /usr/src/app +COPY . /dist +RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ + npm ci --production && \ + rm -f .npmrc + +FROM build as prod +COPY --from=build /dist /dist +CMD ["node","index.js"] + +# The ARG and .npmrc won't appear in the final image but can be found in the Docker daemon un-tagged images list - make sure to delete those +``` + +
    + +

    + +### Code Example Anti Pattern – Using build time args + +
    + +Dockerfile + +``` + +FROM node:12-slim +ARG NPM_TOKEN +WORKDIR /usr/src/app +COPY . /dist +RUN echo "//registry.npmjs.org/:\_authToken=\$NPM_TOKEN" > .npmrc && \ + npm ci --production && \ + rm -f .npmrc + +# Deleting the .npmrc within the same copy command will not save it inside the layer, however it can be found in image history + +CMD ["node","index.js"] +``` + +
    + +

    + +### Blog Quote: "These secrets aren’t saved in the final Docker" + +From the blog, [Alexandra Ulsh](https://www.alexandraulsh.com/2019/02/24/docker-build-secrets-and-npmrc/?fbclid=IwAR0EAr1nr4_QiGzlNQcQKkd9rem19an9atJRO_8-n7oOZXwprToFQ53Y0KQ) + +> In November 2018 Docker 18.09 introduced a new --secret flag for docker build. This allows us to pass secrets from a file to our Docker builds. These secrets aren’t saved in the final Docker image, any intermediate images, or the image commit history. With build secrets, you can now securely build Docker images with private npm packages without build arguments and multi-stage builds. + +``` + +``` diff --git a/sections/docker/bootstrap-using-node.japanese.md b/sections/docker/bootstrap-using-node.japanese.md new file mode 100644 index 000000000..283fd3c19 --- /dev/null +++ b/sections/docker/bootstrap-using-node.japanese.md @@ -0,0 +1,85 @@ +# Bootstrap container using node command instead of npm + +## One paragraph explainer + +We are used to see code examples where folks start their app using `CMD 'npm start'`. This is a bad practice. The `npm` binary will not forward signals to your app which prevents graceful shutdown (see [/sections/docker/graceful-shutdown.md]). If you are using Child-processes they won’t be cleaned up correctly in case of unexpected shutdown, leaving zombie processes on your host. `npm start` also results in having an extra process for no benefit. To start you app use `CMD ['node','server.js']`. If your app spawns child-processes also use `TINI` as an entrypoint. + +### Code example - Bootsraping using Node + +```dockerfile + +FROM node:12-slim AS build + + +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +CMD ["node", "server.js"] +``` + + +### Code example - Using Tiny as entrypoint + +```dockerfile + +FROM node:12-slim AS build + +# Add Tini if using child-processes +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini +RUN chmod +x /tini + +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +ENTRYPOINT ["/tini", "--"] + +CMD ["node", "server.js"] +``` + +### Antipatterns + +Using npm start +```dockerfile + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# don’t do that! +CMD "npm start" +``` + +Using node in a single string will start a bash/ash shell process to execute your command. That is almost the same as using `npm` + +```dockerfile + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# don’t do that, it will start bash +CMD "node server.js" +``` + +Starting with npm, here’s the process tree: +``` +$ ps falx + UID PID PPID COMMAND + 0 1 0 npm + 0 16 1 sh -c node server.js + 0 17 16 \_ node server.js +``` +There is no advantage to those two extra process. + +Sources: + + +https://maximorlov.com/process-signals-inside-docker-containers/ + + +https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#handling-kernel-signals diff --git a/sections/docker/clean-cache.japanese.md b/sections/docker/clean-cache.japanese.md new file mode 100644 index 000000000..485d074d6 --- /dev/null +++ b/sections/docker/clean-cache.japanese.md @@ -0,0 +1,27 @@ +# Clean NODE_MODULE cache + +

    + +### One Paragraph Explainer + +Node package managers, npm & Yarn, cache the installed packages locally so that future projects which need the same libraries won't need to fetch from a remote repository. Although this duplicates the packages and consumes more storage - it pays off in a local development environment that typically keeps installing the same packages. In a Docker container this storage increase is worthless since it installs the dependency only once. By removing this cache, using a single line of code, tens of MB are shaved from the image. While doing so, ensure that it doesn't exit with non-zero code and fail the CI build because of caching issues - This can be avoided by including the --force flag. + +*Please not that this is not relevant if you are using a multi-stage build as long as you don't install new packages in the last stage* + +

    + +### Code Example – Clean cache + +
    +Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm cache clean --force + +# The rest comes here +``` + +
    diff --git a/sections/docker/docker-ignore.japanese.md b/sections/docker/docker-ignore.japanese.md new file mode 100644 index 000000000..37ab9bd0a --- /dev/null +++ b/sections/docker/docker-ignore.japanese.md @@ -0,0 +1,49 @@ +# Use .dockerignore to prevent leaking secrets + +

    + +### One Paragraph Explainer + +The Docker build command copies the local files into the build context environment over a virtual network. Be careful - development and CI folders contain secrets like .npmrc, .aws, .env files and other sensitive files. Consequently, Docker images might hold secrets and expose them in unsafe territories (e.g. Docker repository, partners servers). In a better world the Dockerfile should be explicit about what is being copied. On top of this include a .dockerignore file that acts as the last safety net that filters out unnecessary folders and potential secrets. Doing so also boosts the build speed - By leaving out common development folders that have no use in production (e.g. .git, test results, IDE configuration), the builder can better utilize the cache and achieve better performance + +

    + +### Code Example – A good default .dockerignore for Node.js + +
    +.dockerignore + +``` +**/node_modules/ +**/.git +**/README.md +**/LICENSE +**/.vscode +**/npm-debug.log +**/coverage +**/.env +**/.editorconfig +**/.aws +**/dist +``` + +
    + +

    + +### Code Example Anti-Pattern – Recursive copy of all files + +
    +Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +# The next line copies everything +COPY . . + +# The rest comes here + +``` + +
    diff --git a/sections/docker/generic-tips.japanese.md b/sections/docker/generic-tips.japanese.md new file mode 100644 index 000000000..54570d5c4 --- /dev/null +++ b/sections/docker/generic-tips.japanese.md @@ -0,0 +1,29 @@ +[✔]: ../../assets/images/checkbox-small-blue.png + +# Common Node.js Docker best practices + +This common Docker guidelines section contains best practices that are standardized among all programming languages and have no special Node.js interpretation + +## ![✔] Prefer COPY over ADD command + +**TL;DR:** COPY is safer as it copies local files only while ADD supports fancier fetches like downloading binaries from remote sites + +## ![✔] Avoid updating the base OS + +**TL;DR:** Updating the local binaries during build (e.g. apt-get update) creates inconsistent images every time it runs and also demands elevated privileges. Instead use base images that are updated frequently + +## ![✔] Classify images using labels + +**TL;DR:** Providing metadata for each image might help Ops professionals treat it adequately. For example, include the maintainer name, build date and other information that might prove useful when someone needs to reason about an image + +## ![✔] Use unprivileged containers + +**TL;DR:** Privileged container have the same permissions and capabilities as the root user over the host machine. This is rarely needed and as a rule of thumb one should use the 'node' user that is created within official Node images + +## ![✔] Inspect and verify the final result + +**TL;DR:** Sometimes it's easy to overlook side effects in the build process like leaked secrets or unnecessary files. Inspecting the produced image using tools like [Dive](https://github.com/wagoodman/dive) can easily help to identify such issues + +## ![✔] Perform integrity check + +**TL;DR:** While pulling base or final images, the network might be mislead and redirected to download malicious images. Nothing in the standard Docker protocol prevents this unless signing and verifying the content. [Docker Notary](https://docs.docker.com/notary/getting_started/) is one of the tools to achieve this diff --git a/sections/docker/graceful-shutdown.japanese.md b/sections/docker/graceful-shutdown.japanese.md new file mode 100644 index 000000000..471989bba --- /dev/null +++ b/sections/docker/graceful-shutdown.japanese.md @@ -0,0 +1,84 @@ +# Shutdown gracefully + +

    + +### One Paragraph Explainer + +In a Dockerized runtime like Kubernetes, containers are born and die frequently. This happens not only when errors are thrown but also for good reasons like relocating containers, replacing them with a newer version and more. It's achieved by sending a notice (SIGTERM signal) to the process with a 30 second grace period. This puts a challenge on the developer to ensure the app is handling the ongoing requests and clean-up resources in a timely fashion. Otherwise thousands of sad users will not get a response. Implementation-wise, the shutdown code should wait until all ongoing requests are flushed out and then clean-up resources. Easier said than done, practically it demands orchestrating several parts: Tell the LoadBalancer that the app is not ready to serve more requests (via health-check), wait for existing requests to be done, avoid handling new requests, clean-up resources and finally log some useful information before dying. If Keep-Alive connections are being used, the clients must also be notified that a new connection should be established - A library like [Stoppable](https://github.com/hunterloftis/stoppable) can greatly help achieving this. + +

    + + +### Code Example – Placing Node.js as the root process allows passing signals to the code (see [bootstrap using node](/sections/docker/bootstrap-using-node.md)) + +
    + +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +CMD ["node", "index.js"] +#This line above will make Node.js the root process (PID1) + +``` + +
    + +

    + +### Code Example – Using Tiny process manager to forward signals to Node + +
    + +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +ENV TINI_VERSION v0.19.0 +ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini +RUN chmod +x /tini +ENTRYPOINT ["/tini", "--"] + +CMD ["node", "index.js"] +#Now Node will run a sub-process of TINI which acts as PID1 + +``` + +
    + +

    + +### Code Example Anti Pattern – Using npm scripts to initialize the process + +
    + +Dockerfile + +``` + +FROM node:12-slim + +# Build logic comes here + +CMD ["npm", "start"] +#Now Node will run a sub-process of npm and won't receive signals + +``` + +
    + +

    + +### Example - The shutdown phases + +From the blog, [Rising Stack](https://blog.risingstack.com/graceful-shutdown-node-js-kubernetes/) + +![alt text](/assets/images/Kubernetes-graceful-shutdown-flowchart.png "The shutdown phases") diff --git a/sections/docker/image-tags.japanese.md b/sections/docker/image-tags.japanese.md new file mode 100644 index 000000000..293dabdd4 --- /dev/null +++ b/sections/docker/image-tags.japanese.md @@ -0,0 +1,27 @@ +# Understand image tags vs digests and use the `:latest` tag with caution + +### One Paragraph Explainer + +If this is a production situation and security and stability are important then just "convenience" is likely not the best deciding factor. In addition the `:latest` tag is Docker's default tag. This means that a developer who forgets to add an explicit tag will accidentally push a new version of an image as `latest`, which might end in very unintended results if the `latest` tag is being relied upon as the latest production image. + +### Code example: + +```bash +$ docker build -t company/image_name:0.1 . +# :latest image is not updated +$ docker build -t company/image_name +# :latest image is updated +$ docker build -t company/image_name:0.2 . +# :latest image is not updated +$ docker build -t company/image_name:latest . +# :latest image is updated +``` + +### What Other Bloggers Say +From the blog by [Vladislav Supalov](https://vsupalov.com/docker-latest-tag/): +> Some people expect that :latest always points to the most-recently-pushed version of an image. That’s not true. + +From the [Docker success center](https://success.docker.com/article/images-tagging-vs-digests) +> + +
    diff --git a/sections/docker/install-for-production.japanese.md b/sections/docker/install-for-production.japanese.md new file mode 100644 index 000000000..51ec0e7bc --- /dev/null +++ b/sections/docker/install-for-production.japanese.md @@ -0,0 +1,86 @@ +# Remove development dependencies + +

    + +### One Paragraph Explainer + +Dev dependencies greatly increase the container attack surface (i.e. potential security weakness) and the container size. As an example, some of the most impactful npm security breaches were originated from devDependencies like [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes) or affected dev packages like [event-stream that was used by nodemon](https://snyk.io/blog/a-post-mortem-of-the-malicious-event-stream-backdoor/). For those reasons the image that is finally shipped to production should be safe and minimal. Running npm install with a `--production` is a great start, however it gets even safer to run `npm ci` that ensures a fresh install and the existence of a lock file. Removing the local cache can shave additional tens of MB. Often there is a need to test or debug within a container using devDependencies - In that case, [multi stage builds](/sections/docker/multi_stage_builds.md) can help in having different sets of dependencies and finally only those for production. + +

    + +### Code Example – Installing for production + +
    + +Dockerfile + +``` +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +RUN npm ci --production && npm clean cache --force + +# The rest comes here +``` + +
    + +

    + +### Code Example – Installing for production with multi-stage build + +
    + +Dockerfile + +``` +FROM node:14.8.0-alpine AS build +COPY --chown=node:node package.json package-lock.json ./ +# ✅ Safe install +RUN npm ci +COPY --chown=node:node src ./src +RUN npm run build + +# Run-time stage +FROM node:14.8.0-alpine +COPY --chown=node:node --from=build package.json package-lock.json ./ +COPY --chown=node:node --from=build node_modules ./node_modules +COPY --chown=node:node --from=build dist ./dist + +# ✅ Clean dev packages +RUN npm prune --production + +CMD [ "node", "dist/app.js" ] +``` + +
    + + +

    + +### Code Example Anti-Pattern – Installing all dependencies in a single stage dockerfile + +
    + +Dockerfile + +``` + +FROM node:12-slim AS build +WORKDIR /usr/src/app +COPY package.json package-lock.json ./ +# Two mistakes below: Installing dev dependencies, not deleting the cache after npm install +RUN npm install + +# The rest comes here +``` + +
    + +

    + +### Blog Quote: "npm ci is also more strict than a regular install" + +From [npm documentation](https://docs.npmjs.com/cli/ci.html) + +> This command is similar to npm-install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment – or any situation where you want to make sure you’re doing a clean install of your dependencies. It can be significantly faster than a regular npm install by skipping certain user-oriented features. It is also more strict than a regular install, which can help catch errors or inconsistencies caused by the incrementally-installed local environments of most npm users. diff --git a/sections/docker/lint-dockerfile.japanese.md b/sections/docker/lint-dockerfile.japanese.md new file mode 100644 index 000000000..b9793fd4f --- /dev/null +++ b/sections/docker/lint-dockerfile.japanese.md @@ -0,0 +1,25 @@ +# Lint your Dockerfile + +### One Paragraph Explainer + +As our core application code is linted to conform to best practices and eliminate issues and bugs before it could become a problem, so too should our Dockerfiles. Linting the Dockerfile means increasing the chances of catching production issues on time with very light effort. For example, it can ensure that there aren’t any structural problems with the logic and instructions specified in your Dockerfiles like trying to copy from non-existing stage, copying from unknown online repository, running the app with power user (SUDO) and many more. The Open Source Dockerfile linter [Hadolint](https://github.com/hadolint/hadolint) can be used manually or as part of a CI process to lint your Dockerfile/s. Hadolint is a specialized Dockerfile linter that aims to embrace the [Docker best practices.](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) + + +
    + +### Code example: Inspecting a Dockerfile using hadolint + +```bash +hadolint production.Dockerfile +hadolint --ignore DL3003 --ignore DL3006 # exclude specific rules +hadolint --trusted-registry my-company.com:500 # Warn when using untrusted FROM images +``` + +### What Other Bloggers Say + +From the blog by [Josh Reichardt](https://thepracticalsysadmin.com/lint-your-dockerfiles-with-hadolint/): +> If you haven’t already gotten in to the habit of linting your Dockerfiles you should. Code linting is a common practice in software development which helps find, identify and eliminate issues and bugs before they are ever able to become a problem. One of the main benefits of linting your code is that it helps identify and eliminate nasty little bugs before they ever have a chance to become a problem. + +From the blog by [Jamie Phillips](https://www.phillipsj.net/posts/hadolint-linting-your-dockerfile/) +> Linters are commonly used in development to help teams detect programmatic and stylistic errors. Hadolint is a linter created for Dockerfiles using Haskell. This tool validates against the best practices outlined by Docker and takes a neat approach to parse the Dockerfile that you should checkout. It supports all major platforms, and this tutorial will be leveraging the container to perform the linting on an example Dockerfile. +
    diff --git a/sections/docker/memory-limit.japanese.md b/sections/docker/memory-limit.japanese.md new file mode 100644 index 000000000..f624d018b --- /dev/null +++ b/sections/docker/memory-limit.japanese.md @@ -0,0 +1,70 @@ +# Set memory limits using both Docker and v8 + +

    + +### One Paragraph Explainer + +A memory limit tells the process/container the maximum allowed memory usage - a request or usage beyond this number will kill the process (OOMKill). Applying this is a great practice to ensure one citizen doesn't drink all the juice alone and leaves other components to starve. Memory limits also allow the runtime to place a container in the right instance - placing a container that consumes 500MB in an instance with 300MB memory available will lead to failures. Two different options allow configuring this limit: V8 flags (--max-old-space-size) and the Docker runtime, both are absolutely needed. Ensure to always configure the Docker runtime limits as it has a much wider perspective for making the right health decisions: Given this limit, the runtime knows how to scale and create more resources. It can also make a thoughtful decision on when to crash - if a container has a short burst in memory request and the hosting instance is capable of supporting this, Docker will let the container stay alive. Last, with Docker the Ops experts can set various production memory configurations that can be taken into account like memory swap. This by itself won't be enough - Without setting v8's --max-old-space-size, the JavaScript runtime won't push the garbage collection when getting close to the limits and will also crash when utilizing only 50-60% of the host environment. Consequently, set v8's limit to be 75-100% of Docker's memory limit. + +

    + +### Code Example – Memory limit with Docker + +
    +Bash + +``` +docker run --memory 512m my-node-app +``` + +
    + +

    + +### Code Example – Memory limit with Kubernetes and v8 + +
    +Kubernetes deployment yaml + +``` +apiVersion: v1 +kind: Pod +metadata: + name: my-node-app +spec: + containers: + - name: my-node-app + image: my-node-app + resources: + requests: + memory: "400Mi" + limits: + memory: "500Mi" + command: ["node index.js --max-old-space-size=350"] +``` + +
    + +

    + +### Kubernetes documentation: "If you do not specify a memory limit" + +From [K8S documentation](https://kubernetes.io/docs/tasks/configure-pod-container/assign-memory-resource/) + +> The Container has no upper bound on the amount of memory it uses. The Container could use all of the memory available on the Node where it is running which in turn could invoke the OOM Killer. Further, in case of an OOM Kill, a container with no resource limits will have a greater chance of being killed. + +

    + +### Docker documentation: "it throws an OOME and starts killing processes " + +From [Docker official docs](https://docs.docker.com/config/containers/resource_constraints/) + +> It is important not to allow a running container to consume too much of the host machine’s memory. On Linux hosts, if the kernel detects that there is not enough memory to perform important system functions, it throws an OOME, or Out Of Memory Exception, and starts killing processes to free up memory. + +

    + +### Node.js documentation: "V8 will spend more time on garbage collection" + +From [Node.js official docs](https://nodejs.org/api/cli.html#cli_max_old_space_size_size_in_megabytes) + +> Sets the max memory size of V8's old memory section. As memory consumption approaches the limit, V8 will spend more time on garbage collection in an effort to free unused memory. On a machine with 2GB of memory, consider setting this to 1536 (1.5GB) to leave some memory for other uses and avoid swapping. diff --git a/sections/docker/multi_stage_builds.japanese.md b/sections/docker/multi_stage_builds.japanese.md new file mode 100644 index 000000000..eccba3bf0 --- /dev/null +++ b/sections/docker/multi_stage_builds.japanese.md @@ -0,0 +1,116 @@ +# Use multi-stage builds + +### One Paragraph Explainer + +Multi-stage builds allow to separate build- and runtime-specific environment details, such as available binaries, exposed environment variables, and even the underlying operating system. Splitting up your Dockerfiles into multiple stages will help to reduce final image and container size as you'll only ship what you really need to run your application. Sometimes you'll need to include tools that are only needed during the build phase, for example development dependencies such as the TypeScript CLI. You can install it during the build stage and only use the final output in the run stage. This also means your image will shrink as some dependencies won't get copied over. You might also have to expose environment variables during build that should not be present at runtime (see [avoid build time secrets](/sections/docker/avoid-build-time-secrets.md)), such as API Keys and secrets used for communicating with specific services. In the final stage, you can copy in pre-built resources such as your build folder, or production-only dependencies (which you can also fetch in a subsequent step). + +### Example + +Let's imagine the following directory structure + +``` +- Dockerfile +- src/ + - index.ts +- package.json +- yarn.lock +- .dockerignore +- docs/ + - README.md +``` + +Your [.dockerignore](/sections/docker/dockerignore.md) will already filter out files that won't be needed for building and running your application. + +``` +# Don't copy in existing node_modules, we'll fetch our own +node_modules + +# Docs are large, we don't need them in our Docker image +docs +``` + +#### Dockerfile with multiple stages + +Since Docker is often used in continuous integration environments it is recommended to use the `npm ci` command (instead of `npm install`). It is faster, stricter and reduces inconsistencies by using only the versions specified in the package-lock.json file. See [here](https://docs.npmjs.com/cli/ci.html#description) for more info. This example uses yarn as package manager for which the equivalent to `npm ci` is the `yarn install --frozen-lockfile` [command](https://classic.yarnpkg.com/en/docs/cli/install/). + +```dockerfile +FROM node:14.4.0 AS build + +COPY --chown=node:node . . +RUN yarn install --frozen-lockfile && yarn build + +FROM node:14.4.0 + +USER node +EXPOSE 8080 + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ +RUN yarn install --frozen-lockfile --production + +CMD [ "node", "dist/app.js" ] +``` + +#### Dockerfile with multiple stages and different base images + +```dockerfile +FROM node:14.4.0 AS build + +COPY --chown=node:node . . +RUN yarn install --frozen-lockfile && yarn build + +# This will use a minimal base image for the runtime +FROM node:14.4.0-alpine + +USER node +EXPOSE 8080 + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/yarn.lock ./ +RUN yarn install --frozen-lockfile --production + +CMD [ "node", "dist/app.js" ] +``` + +#### Full Dockerfile with multiple stages and different base images + +Our Dockerfile will contain two phases: One for building the application using the fully-featured Node.js Docker image, +and a second phase for running the application, based on the minimal Alpine image. We'll only copy over the built files to our second stage, +and then install production dependencies. + +```dockerfile +# Start with fully-featured Node.js base image +FROM node:14.4.0 AS build + +USER node +WORKDIR /home/node/app + +# Copy dependency information and install all dependencies +COPY --chown=node:node package.json yarn.lock ./ + +RUN yarn install --frozen-lockfile + +# Copy source code (and all other relevant files) +COPY --chown=node:node src ./src + +# Build code +RUN yarn build + +# Run-time stage +FROM node:14.4.0-alpine + +# Set non-root user and expose port 8080 +USER node +EXPOSE 8080 + +WORKDIR /home/node/app + +# Copy dependency information and install production-only dependencies +COPY --chown=node:node package.json yarn.lock ./ +RUN yarn install --frozen-lockfile --production + +# Copy results from previous stage +COPY --chown=node:node --from=build /home/node/app/dist ./dist + +CMD [ "node", "dist/app.js" ] +``` diff --git a/sections/docker/restart-and-replicate-processes.japanese.md b/sections/docker/restart-and-replicate-processes.japanese.md new file mode 100644 index 000000000..eaf8be505 --- /dev/null +++ b/sections/docker/restart-and-replicate-processes.japanese.md @@ -0,0 +1,44 @@ +# Let the Docker orchestrator restart and replicate processes + +

    + +### One Paragraph Explainer + +Docker runtime orchestrators like Kubernetes are really good at making containers health and placement decisions: They will take care to maximize the number of containers, balance them across zones, and take into account many cluster factors while making these decisions. Goes without words, they identify failing processes (i.e., containers) and restart them in the right place. Despite that some may be tempted to use custom code or tools to replicate the Node process for CPU utilization or restart the process upon failure (e.g., Cluster module, PM2). These local tools don't have the perspective and the data that is available on the cluster level. For example, when the instances resources can host 3 containers and given 2 regions or zones, Kubernetes will take care to spread the containers across zones. This way, in case of a zonal or regional failure, the app will stay alive. On the contrary side when using local tools for restarting the process the Docker orchestrator is not aware of the errors and can not make thoughtful decisions like relocating the container to a new instance or zone. + +

    + +### Code Example – Invoking Node.js directly without intermediate tools + +
    + +Dockerfile + +``` + +FROM node:12-slim + +# The build logic comes here + +CMD ["node", "index.js"] +``` + +
    + +

    + +### Code Example Anti Pattern – Using a process manager + +
    + +Dockerfile + +``` +FROM node:12-slim + +# The build logic comes here + +CMD ["pm2-runtime", "indes.js"] +``` + +
    diff --git a/sections/docker/scan-images.japanese.md b/sections/docker/scan-images.japanese.md new file mode 100644 index 000000000..3e09f81a4 --- /dev/null +++ b/sections/docker/scan-images.japanese.md @@ -0,0 +1,30 @@ +# Scan the entire image before production + +

    + +### One Paragraph Explainer + +Scanning the code for vulnerabilities is a valuable act but it doesn't cover all the potential threats. Why? Because vulnerabilities also exist on the OS level and the app might execute those binaries like Shell, Tarball, OpenSSL. Also, vulnerable dependencies might be injected after the code scan (i.e. supply chain attacks) - hence scanning the final image just before production is in order. This idea resembles E2E tests - after testing the various pieces in-isolation, it's valuable to finally check the assembled deliverable. There are 3 main scanner families: Local/CI binaries with a cached vulnerabilities DB, scanners as a service in the cloud and a niche of tools which scan during the docker build itself. The first group is the most popular and usually the fastest - Tools like [Trivvy](https://github.com/aquasecurity/trivy), [Anchore](https://github.com/anchore/anchore) and [Snyk](https://support.snyk.io/hc/en-us/articles/360003946897-Container-security-overview) are worth exploring. Most CI vendors provide a local plugin that facilitates the interaction with these scanners. It should be noted that these scanners cover a lot of ground and therefore will show findings in almost every scan - consider setting a high threshold bar to avoid getting overwhelmed + +

    + +### Code Example – Scanning with Trivvy + +
    + +Bash + +``` +sudo apt-get install rpm +$ wget https://github.com/aquasecurity/trivy/releases/download/{TRIVY_VERSION}/trivy_{TRIVY_VERSION}_Linux-64bit.deb +$ sudo dpkg -i trivy_{TRIVY_VERSION}_Linux-64bit.deb +trivy image [YOUR_IMAGE_NAME] +``` + +
    + +

    + +### Report Example – Docker scan results (By Anchore) + +![Report examples](/assets/images/anchore-report.png "Docker scan report") diff --git a/sections/docker/smaller_base_images.japanese.md b/sections/docker/smaller_base_images.japanese.md new file mode 100644 index 000000000..d394954d3 --- /dev/null +++ b/sections/docker/smaller_base_images.japanese.md @@ -0,0 +1,13 @@ +# Prefer smaller Docker base images + +Large Docker images can lead to higher exposure to vulnerabilities and increased resource consumption. Often you don't need certain packages installed at runtime that are needed for building. +Pulling and storing larger images will become more expensive at scale, when dealing with larger images. By design minimal images may not come with common libraries needed for building native modules or packages useful for debugging (e.g. curl) pre-installed. +Using the Alpine Linux variants of images can lead to a reduced footprint in terms of resources used and the amount of attack vectors present in fully-featured systems. The Node.js v14.4.0 Docker image is ~345MB in size versus ~39MB for the Alpine version, which is almost 10x smaller. +A Slim variant based on Debian, which is only 38MB in size and contains the minimal packages needed to run Node.js, is also a great choice. + +### Blog Quote: "If you want to shrink your Docker images, have your services start faster and be more secure then try Alpine out." + +From [Nick Janetakis' blog](https://nickjanetakis.com/blog/the-3-biggest-wins-when-using-alpine-as-a-base-docker-image) + +> It’s no secret by now that Docker is heavily using Alpine as a base image for official Docker images. This movement started near the beginning of 2016. [...] + When pulling down new Docker images onto a fresh server, you can expect the initial pull to be quite a bit faster on Alpine. The slower your network is, the bigger the difference it will be. [...] Another perk of being much smaller in size is that the surface area to be attacked is much less. When there’s not a lot of packages and libraries on your system, there’s very little that can go wrong. diff --git a/sections/docker/use-cache-for-shorter-build-time.japanese.md b/sections/docker/use-cache-for-shorter-build-time.japanese.md new file mode 100644 index 000000000..3e7ffaae3 --- /dev/null +++ b/sections/docker/use-cache-for-shorter-build-time.japanese.md @@ -0,0 +1,115 @@ +# Leverage caching to reduce build times + +## One paragraph explainer + +Docker images are a combination of layers, each instruction in your Dockerfile creates a layer. The docker daemon can reuse those layers between builds if the instructions are identical or in the case of a `COPY` or `ADD` files used are identical. ⚠️ If the cache can't be used for a particular layer all the subsequent layers will be invalidated too. That's why order is important. It is crucial to layout your Dockerfile correctly to reduce the number of moving parts in your build; the less updated instructions should be at the top and the ones constantly changing (like app code) should be at the bottom. It's also important to think that instructions that trigger long operation should be close to the top to ensure they happen only when really necessary (unless it changes every time you build your docker image). Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. + +![Docker layers](/assets/images/docker_layers_schema.png) + +* Image taken from [Digging into Docker layers](https://medium.com/@jessgreb01/digging-into-docker-layers-c22f948ed612) by jessgreb01* + +### Rules + +#### Avoid LABEL that change all the time + +If you have a label containing the build number at the top of your Dockerfile, the cache will be invalidated at every build + +```Dockerfile +#Beginning of the file +FROM node:10.22.0-alpine3.11 as builder + +# Don't do that here! +LABEL build_number="483" + +#... Rest of the Dockerfile +``` + +#### Have a good .dockerignore file + +[**See: On the importance of docker ignore**](/sections/docker/docker-ignore.md) + +The docker ignore avoids copying files that could bust our cache logic, like tests results reports, logs or temporary files. + +#### Install "system" packages first + +It is recommended to create a base docker image that has all the system packages you use. If you **really** need to install packages using `apt`,`yum`,`apk` or the likes, this should be one of the first instructions. You don't want to reinstall make,gcc or g++ every time you build your node app. +**Do not install package only for convenience, this is a production app.** + +#### First, only ADD your package.json and your lockfile + +```Dockerfile +COPY "package.json" "package-lock.json" "./" +RUN npm ci +``` + +The lockfile and the package.json change less often. Copying them first will keep the `npm install` step in the cache, this saves precious time. + +### Then copy your files and run build step (if needed) + +```Dockerfile +COPY . . +RUN npm run build +``` + +## Examples + +### Basic Example with node_modules needing OS dependencies +```Dockerfile +#Create node image version alias +FROM node:10.22.0-alpine3.11 as builder + +RUN apk add --no-cache \ + build-base \ + gcc \ + g++ \ + make + +USER node +WORKDIR /app +COPY "package.json" "package-lock.json" "./" +RUN npm ci --production +COPY . "./" + +FROM node as app +USER node +WORKDIR /app +COPY --from=builder /app/ "./" +RUN npm prune --production + +CMD ["node", "dist/server.js"] +``` + + +### Example with a build step (when using typescript for example) +```Dockerfile +#Create node image version alias +FROM node:10.22.0-alpine3.11 as builder + +RUN apk add --no-cache \ + build-base \ + gcc \ + g++ \ + make + +USER node +WORKDIR /app +COPY "package.json" "package-lock.json" "./" +RUN npm ci +COPY . . +RUN npm run build + +FROM node as app +USER node +WORKDIR /app +# Only copying the files that we need +COPY --from=builder /app/node_modules node_modules +COPY --from=builder /app/package.json . +COPY --from=builder /app/dist dist +RUN npm prune --production + +CMD ["node", "dist/server.js"] +``` + +## Useful links + +Docker docks: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#leverage-build-cache diff --git a/sections/errorhandling/apmproducts.japanese.md b/sections/errorhandling/apmproducts.japanese.md new file mode 100644 index 000000000..5f302168d --- /dev/null +++ b/sections/errorhandling/apmproducts.japanese.md @@ -0,0 +1,28 @@ +# Discover errors and downtime using APM products + + +### One Paragraph Explainer + +Exception != Error. Traditional error handling assumes the existence of exception as a code related problem but application errors might come in the form of slow code paths, API downtime, lack of computational resources and more. This is where APM products come in handy as they allow to detect a wide variety of ‘burried’ issues proactively with a minimal setup. Among the common features of APM products are for example alerting when the HTTP API returns errors, detect when the API response time drops below some threshold, detection of ‘code smells’, features to monitor server resources, operational intelligence dashboard with IT metrics and many other useful features. Most vendors offer a free plan. + +### Wikipedia about APM + +In the fields of information technology and systems management, Application Performance Management (APM) is the monitoring and management of performance and availability of software applications. APM strives to detect and diagnose complex application performance problems to maintain an expected level of service. APM is “the translation of IT metrics into business meaning ([i.e.] value)". Major products and segments. + +### Understanding the APM marketplace + +APM products constitute 3 major segments: + +1. Website or API monitoring – external services that constantly monitor uptime and performance via HTTP requests. Can be set up in few minutes. Following are few selected contenders: [Pingdom](https://www.pingdom.com/), [Uptime Robot](https://uptimerobot.com/), and [New Relic](https://newrelic.com/application-monitoring) + +2. Code instrumentation – product family which requires embedding an agent within the application to use features like slow code detection, exception statistics, performance monitoring and many more. Following are few selected contenders: New Relic, App Dynamics + +3. Operational intelligence dashboard – this line of products is focused on facilitating the ops team with metrics and curated content that helps to easily stay on top of application performance. This usually involves aggregating multiple sources of information (application logs, DB logs, servers log, etc) and upfront dashboard design work. Following are few selected contenders: [Datadog](https://www.datadoghq.com/), [Splunk](https://www.splunk.com/), [Zabbix](https://www.zabbix.com/) + + + + ### Example: UpTimeRobot.Com – Website monitoring dashboard +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/uptimerobot.jpg "Website monitoring dashboard") + + ### Example: AppDynamics.Com – end to end monitoring combined with code instrumentation +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/app-dynamics-dashboard.png "end to end monitoring combined with code instrumentation") diff --git a/sections/errorhandling/asyncerrorhandling.japanese.md b/sections/errorhandling/asyncerrorhandling.japanese.md new file mode 100644 index 000000000..795dcea39 --- /dev/null +++ b/sections/errorhandling/asyncerrorhandling.japanese.md @@ -0,0 +1,109 @@ +# Use Async-Await or promises for async error handling + +### One Paragraph Explainer + +Callbacks don’t scale well since most programmers are not familiar with them. They force to check errors all over, deal with nasty code nesting and make it difficult to reason about the code flow. Promise libraries like BlueBird, async, and Q pack a standard code style using RETURN and THROW to control the program flow. Specifically, they support the favorite try-catch error handling style which allows freeing the main code path from dealing with errors in every function + +### Code Example – using promises to catch errors + +```javascript +return functionA() + .then(functionB) + .then(functionC) + .then(functionD) + .catch((err) => logger.error(err)) + .then(alwaysExecuteThisFunction) +``` + + +### Code Example - using async/await to catch errors + +```javascript +async function executeAsyncTask () { + try { + const valueA = await functionA(); + const valueB = await functionB(valueA); + const valueC = await functionC(valueB); + return await functionD(valueC); + } + catch (err) { + logger.error(err); + } finally { + await alwaysExecuteThisFunction(); + } +} +``` + +### Anti pattern code example – callback style error handling + +
    +Javascript + +```javascript +getData(someParameter, function(err, result) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(a, function(err, result) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(b, function(c) { + getMoreData(d, function(e) { + if(err !== null ) { + // you get the idea? + } + }) + }); + } + }); + } +}); +``` +
    + +
    +Typescript + +```typescript +getData(someParameter, function(err: Error | null, resultA: ResultA) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(resultA, function(err: Error | null, resultB: ResultB) { + if(err !== null) { + // do something like calling the given callback function and pass the error + getMoreData(resultB, function(resultC: ResultC) { + getMoreData(resultC, function(err: Error | null, d: ResultD) { + if(err !== null) { + // you get the idea? + } + }) + }); + } + }); + } +}); +``` +
    + +### Blog Quote: "We have a problem with promises" + + From the blog pouchdb.com + + > ……And in fact, callbacks do something even more sinister: they deprive us of the stack, which is something we usually take for granted in programming languages. Writing code without a stack is a lot like driving a car without a brake pedal: you don’t realize how badly you need it until you reach for it and it’s not there. The whole point of promises is to give us back the language fundamentals we lost when we went async: return, throw, and the stack. But you have to know how to use promises correctly in order to take advantage of them. + +### Blog Quote: "The promises method is much more compact" + + From the blog gosquared.com + + > ………The promises method is much more compact, clearer and quicker to write. If an error or exception occurs within any of the ops it is handled by the single .catch() handler. Having this single place to handle all errors means you don’t need to write error checking for each stage of the work. + +### Blog Quote: "Promises are native ES6, can be used with generators" + + From the blog StrongLoop + + > ….Callbacks have a lousy error-handling story. Promises are better. Marry the built-in error handling in Express with promises and significantly lower the chances of an uncaught exception. Promises are native ES6, can be used with generators, and ES7 proposals like async/await through compilers like Babel + +### Blog Quote: "All those regular flow control constructs you are used to are completely broken" + +From the blog Benno’s + + > ……One of the best things about asynchronous, callback-based programming is that basically all those regular flow control constructs you are used to are completely broken. However, the one I find most broken is the handling of exceptions. Javascript provides a fairly familiar try…catch construct for dealing with exceptions. The problem with exceptions is that they provide a great way of short-cutting errors up a call stack, but end up being completely useless if the error happens on a different stack… diff --git a/sections/errorhandling/catchunhandledpromiserejection.japanese.md b/sections/errorhandling/catchunhandledpromiserejection.japanese.md new file mode 100644 index 000000000..540aeee52 --- /dev/null +++ b/sections/errorhandling/catchunhandledpromiserejection.japanese.md @@ -0,0 +1,87 @@ +# Catch unhandled promise rejections + +

    + +### One Paragraph Explainer + +Typically, most of modern Node.js/Express application code runs within promises – whether within the .then handler, a function callback or in a catch block. Surprisingly, unless a developer remembered to add a .catch clause, errors thrown at these places are not handled by the uncaughtException event-handler and disappear. Recent versions of Node added a warning message when an unhandled rejection pops, though this might help to notice when things go wrong but it's obviously not a proper error handling method. The straightforward solution is to never forget adding .catch clauses within each promise chain call and redirect to a centralized error handler. However, building your error handling strategy only on developer’s discipline is somewhat fragile. Consequently, it’s highly recommended using a graceful fallback and subscribe to `process.on('unhandledRejection', callback)` – this will ensure that any promise error, if not handled locally, will get its treatment. + +

    + +### Code example: these errors will not get caught by any error handler (except unhandledRejection) + +```javascript +DAL.getUserById(1).then((johnSnow) => { + // this error will just vanish + if(johnSnow.isAlive === false) + throw new Error('ahhhh'); +}); +``` + +

    + +### Code example: Catching unresolved and rejected promises + +
    +Javascript + +```javascript +process.on('unhandledRejection', (reason, p) => { + // I just caught an unhandled promise rejection, + // since we already have fallback handler for unhandled errors (see below), + // let throw and let him handle that + throw reason; +}); + +process.on('uncaughtException', (error) => { + // I just received an error that was never handled, time to handle it and then decide whether a restart is needed + errorManagement.handler.handleError(error); + if (!errorManagement.handler.isTrustedError(error)) + process.exit(1); +}); +``` +
    + +
    +Typescript + +```typescript +process.on('unhandledRejection', (reason: string, p: Promise) => { + // I just caught an unhandled promise rejection, + // since we already have fallback handler for unhandled errors (see below), + // let throw and let him handle that + throw reason; +}); + +process.on('uncaughtException', (error: Error) => { + // I just received an error that was never handled, time to handle it and then decide whether a restart is needed + errorManagement.handler.handleError(error); + if (!errorManagement.handler.isTrustedError(error)) + process.exit(1); +}); +``` +
    + +

    + +### Blog Quote: "If you can make a mistake, at some point you will" + + From the blog James Nelson + + > Let’s test your understanding. Which of the following would you expect to print an error to the console? + +```javascript +Promise.resolve('promised value').then(() => { + throw new Error('error'); +}); + +Promise.reject('error value').catch(() => { + throw new Error('error'); +}); + +new Promise((resolve, reject) => { + throw new Error('error'); +}); +``` + +> I don’t know about you, but my answer is that I’d expect all of them to print an error. However, the reality is that a number of modern JavaScript environments won’t print errors for any of them.The problem with being human is that if you can make a mistake, at some point you will. Keeping this in mind, it seems obvious that we should design things in such a way that mistakes hurt as little as possible, and that means handling errors by default, not discarding them. diff --git a/sections/errorhandling/centralizedhandling.japanese.md b/sections/errorhandling/centralizedhandling.japanese.md new file mode 100644 index 000000000..1d184626d --- /dev/null +++ b/sections/errorhandling/centralizedhandling.japanese.md @@ -0,0 +1,164 @@ +# Handle errors centrally. Not within middlewares + +### One Paragraph Explainer + +Without one dedicated object for error handling, greater are the chances of important errors hiding under the radar due to improper handling. The error handler object is responsible for making the error visible, for example by writing to a well-formatted logger, sending events to some monitoring product like [Sentry](https://sentry.io/), [Rollbar](https://rollbar.com/), or [Raygun](https://raygun.com/). Most web frameworks, like [Express](http://expressjs.com/en/guide/error-handling.html#writing-error-handlers), provide an error handling middleware mechanism. A typical error handling flow might be: Some module throws an error -> API router catches the error -> it propagates the error to the middleware (e.g. Express, KOA) who is responsible for catching errors -> a centralized error handler is called -> the middleware is being told whether this error is an untrusted error (not operational) so it can restart the app gracefully. Note that it’s a common, yet wrong, practice to handle errors within Express middleware – doing so will not cover errors that are thrown in non-web interfaces. + +### Code Example – a typical error flow + +
    +Javascript + +```javascript +// DAL layer, we don't handle errors here +DB.addDocument(newCustomer, (error, result) => { + if (error) + throw new Error('Great error explanation comes here', other useful parameters) +}); + +// API route code, we catch both sync and async errors and forward to the middleware +try { + customerService.addNew(req.body).then((result) => { + res.status(200).json(result); + }).catch((error) => { + next(error) + }); +} +catch (error) { + next(error); +} + +// Error handling middleware, we delegate the handling to the centralized error handler +app.use(async (err, req, res, next) => { + const isOperationalError = await errorHandler.handleError(err); + if (!isOperationalError) { + next(err); + } +}); +``` +
    + +
    +Typescript + +```typescript +// DAL layer, we don't handle errors here +DB.addDocument(newCustomer, (error: Error, result: Result) => { + if (error) + throw new Error('Great error explanation comes here', other useful parameters) +}); + +// API route code, we catch both sync and async errors and forward to the middleware +try { + customerService.addNew(req.body).then((result: Result) => { + res.status(200).json(result); + }).catch((error: Error) => { + next(error) + }); +} +catch (error) { + next(error); +} + +// Error handling middleware, we delegate the handling to the centralized error handler +app.use(async (err: Error, req: Request, res: Response, next: NextFunction) => { + const isOperationalError = await errorHandler.handleError(err); + if (!isOperationalError) { + next(err); + } +}); +``` +
    + + +### Code example – handling errors within a dedicated object + +
    +Javascript + +```javascript +module.exports.handler = new errorHandler(); + +function errorHandler() { + this.handleError = async (err) => { + await logger.logError(err); + await sendMailToAdminIfCritical; + await saveInOpsQueueIfCritical; + await determineIfOperationalError; + }; +} +``` +
    + +
    +Typescript + +```typescript +class ErrorHandler { + public async handleError(err: Error): Promise { + await logger.logError(err); + await sendMailToAdminIfCritical(); + await saveInOpsQueueIfCritical(); + await determineIfOperationalError(); + }; +} + +export const handler = new ErrorHandler(); +``` +
    + + +### Code Example – Anti Pattern: handling errors within the middleware + +
    +Javascript + +```javascript +// middleware handling the error directly, who will handle Cron jobs and testing errors? +app.use((err, req, res, next) => { + logger.logError(err); + if (err.severity == errors.high) { + mailer.sendMail(configuration.adminMail, 'Critical error occured', err); + } + if (!err.isOperational) { + next(err); + } +}); +``` +
    + + +
    +Typescript + +```typescript +// middleware handling the error directly, who will handle Cron jobs and testing errors? +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + logger.logError(err); + if (err.severity == errors.high) { + mailer.sendMail(configuration.adminMail, 'Critical error occured', err); + } + if (!err.isOperational) { + next(err); + } +}); +``` +
    + +### Blog Quote: "Sometimes lower levels can’t do anything useful except propagate the error to their caller" + +From the blog Joyent, ranked 1 for the keywords “Node.js error handling” + +> …You may end up handling the same error at several levels of the stack. This happens when lower levels can’t do anything useful except propagate the error to their caller, which propagates the error to its caller, and so on. Often, only the top-level caller knows what the appropriate response is, whether that’s to retry the operation, report an error to the user, or something else. But that doesn’t mean you should try to report all errors to a single top-level callback, because that callback itself can’t know in what context the error occurred… + +### Blog Quote: "Handling each err individually would result in tremendous duplication" + +From the blog JS Recipes ranked 17 for the keywords “Node.js error handling” + +> ……In Hackathon Starter api.js controller alone, there are over 79 occurrences of error objects. Handling each err individually would result in a tremendous amount of code duplication. The next best thing you can do is to delegate all error handling logic to an Express middleware… + +### Blog Quote: "HTTP errors have no place in your database code" + +From the blog Daily JS ranked 14 for the keywords “Node.js error handling” + +> ……You should set useful properties in error objects, but use such properties consistently. And, don’t cross the streams: HTTP errors have no place in your database code. Or for browser developers, Ajax errors have a place in the code that talks to the server, but not code that processes Mustache templates… diff --git a/sections/errorhandling/documentingusingswagger.japanese.md b/sections/errorhandling/documentingusingswagger.japanese.md new file mode 100644 index 000000000..3341b0c13 --- /dev/null +++ b/sections/errorhandling/documentingusingswagger.japanese.md @@ -0,0 +1,52 @@ +# Document API errors using Swagger or GraphQL + +### One Paragraph Explainer + +REST APIs return results using HTTP status codes, it’s absolutely required for the API user to be aware not only about the API schema but also about potential errors – the caller may then catch an error and tactfully handle it. For example, your API documentation might state in advance that HTTP status 409 is returned when the customer name already exists (assuming the API register new users) so the caller can correspondingly render the best UX for the given situation. Swagger is a standard that defines the schema of API documentation offering an eco-system of tools that allow creating documentation easily online, see print screens below + +If you have already adopted GraphQL for your API endpoints, your schema already contains strict guarantees as to what errors should look like ([outlined in the spec](https://facebook.github.io/graphql/June2018/#sec-Errors)) and how they should be handled by your client-side tooling. In addition, you can also supplement them with comment-based documentation. + +### GraphQL Error Example + +> This example uses [SWAPI](https://graphql.org/swapi-graphql), the Star Wars API. + +```graphql +# should fail because id is not valid +{ + film(id: "1ZmlsbXM6MQ==") { + title + } +} +``` + +```json +{ + "errors": [ + { + "message": "No entry in local cache for https://swapi.co/api/films/.../", + "locations": [ + { + "line": 2, + "column": 3 + } + ], + "path": [ + "film" + ] + } + ], + "data": { + "film": null + } +} +``` + +### Blog Quote: "You have to tell your callers what errors can happen" + +From the blog Joyent, ranked 1 for the keywords “Node.js logging” + + > We’ve talked about how to handle errors, but when you’re writing a new function, how do you deliver errors to the code that called your function? …If you don’t know what errors can happen or don’t know what they mean, then your program cannot be correct except by accident. So if you’re writing a new function, you have to tell your callers what errors can happen and what they mean… + +### Useful Tool: Swagger Online Documentation Creator + +![Swagger API Scheme](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/swaggerDoc.png "API error handling") diff --git a/sections/errorhandling/failfast.japanese.md b/sections/errorhandling/failfast.japanese.md new file mode 100644 index 000000000..805d7ac9b --- /dev/null +++ b/sections/errorhandling/failfast.japanese.md @@ -0,0 +1,67 @@ +# Fail fast, validate arguments using a dedicated library + +### One Paragraph Explainer + +We all know how checking arguments and failing fast is important to avoid hidden bugs (see anti-pattern code example below). If not, read about explicit programming and defensive programming. In reality, we tend to avoid it due to the annoyance of coding it (e.g. think of validating hierarchical JSON object with fields like email and dates) – libraries like Joi and Validator turn this tedious task into a breeze. + +### Wikipedia: Defensive Programming + +Defensive programming is an approach to improve software and source code, in terms of General quality – reducing the number of software bugs and problems. Making the source code comprehensible – the source code should be readable and understandable so it is approved in a code audit. Making the software behave in a predictable manner despite unexpected inputs or user actions. + +### Code example: validating complex JSON input using ‘Joi’ + +```javascript +var memberSchema = Joi.object().keys({ + password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), + birthyear: Joi.number().integer().min(1900).max(2013), + email: Joi.string().email() +}); + +function addNewMember(newMember) { + // assertions come first + Joi.assert(newMember, memberSchema); //throws if validation fails + // other logic here +} +``` + + + +### Anti-pattern: no validation yields nasty bugs + +
    +Javascript + +```javascript +// if the discount is positive let's then redirect the user to print his discount coupons +function redirectToPrintDiscount(httpResponse, member, discount) { + if (discount != 0) { + httpResponse.redirect(`/discountPrintView/${member.id}`); + } +} + +redirectToPrintDiscount(httpResponse, someMember); +// forgot to pass the parameter discount, why the heck was the user redirected to the discount screen? +``` +
    + +
    +Typescript + +```typescript +// if the discount is positive let's then redirect the user to print his discount coupons +function redirectToPrintDiscount(httpResponse: Response, member: Member, discount: number) { + if (discount != 0) { + httpResponse.redirect(`/discountPrintView/${member.id}`); + } +} + +redirectToPrintDiscount(httpResponse, someMember, -12); +// We passed a negative parameter discount, why the heck was the user redirected to the discount screen? +``` +
    + +### Blog Quote: "You should throw these errors immediately" + + From the blog: Joyent + + > A degenerate case is where someone calls an asynchronous function but doesn’t pass a callback. You should throw these errors immediately since the program is broken and the best chance of debugging it involves getting at least a stack trace and ideally a core file at the point of the error. To do this, we recommend validating the types of all arguments at the start of the function. diff --git a/sections/errorhandling/monitoring.japanese.md b/sections/errorhandling/monitoring.japanese.md new file mode 100644 index 000000000..57213a831 --- /dev/null +++ b/sections/errorhandling/monitoring.japanese.md @@ -0,0 +1,17 @@ +# Monitoring + +### One Paragraph Explainer + +> At the very basic level, monitoring means you can *easily identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the number of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw data to custom BI clients, Slack notifications and many others. + +Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, newrelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. AWS CloudWatch, Google StackDriver) will tell you immediately about the hardware metric but nothing about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack by default the hardware view. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to Elastic stack and configure some additional agent (e.g. Beat) to share hardware-related information to get the full picture. + +### Blog Quote: "We have a problem with promises" + + From the blog, pouchdb.com ranked 11 for the keywords “Node Promises” + + > … We recommend you to watch these signals for all of your services: Error Rate: Because errors are user facing and immediately affect your customers. +Response time: Because the latency directly affects your customers and business. +Throughput: The traffic helps you to understand the context of increased error rates and the latency too. +Saturation: It tells how “full” your service is. If the CPU usage is 90%, can your system handle more traffic? +… diff --git a/sections/errorhandling/operationalvsprogrammererror.japanese.md b/sections/errorhandling/operationalvsprogrammererror.japanese.md new file mode 100644 index 000000000..69ccee2c3 --- /dev/null +++ b/sections/errorhandling/operationalvsprogrammererror.japanese.md @@ -0,0 +1,85 @@ +# Distinguish operational vs programmer errors + +### One Paragraph Explainer + +Distinguishing the following two error types will minimize your app downtime and helps avoid crazy bugs: Operational errors refer to situations where you understand what happened and the impact of it – for example, a query to some HTTP service failed due to connection problem. On the other hand, programmer errors refer to cases where you have no idea why and sometimes where an error came from – it might be some code that tried to read an undefined value or DB connection pool that leaks memory. Operational errors are relatively easy to handle – usually logging the error is enough. Things become hairy when a programmer error pops up, the application might be in an inconsistent state and there’s nothing better you can do than to restart gracefully + +### Code Example – marking an error as operational (trusted) + +
    +Javascript + +```javascript +// marking an error object as operational +const myError = new Error('How can I add new product when no value provided?'); +myError.isOperational = true; + +// or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object") +class AppError { + constructor (commonType, description, isOperational) { + Error.call(this); + Error.captureStackTrace(this); + this.commonType = commonType; + this.description = description; + this.isOperational = isOperational; + } +}; + +throw new AppError(errorManagement.commonErrors.InvalidInput, 'Describe here what happened', true); + +``` +
    + +
    +Typescript + +```typescript +// some centralized error factory (see other examples at the bullet "Use only the built-in Error object") +export class AppError extends Error { + public readonly commonType: string; + public readonly isOperational: boolean; + + constructor(commonType: string, description: string, isOperational: boolean) { + super(description); + + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + + this.commonType = commonType; + this.isOperational = isOperational; + + Error.captureStackTrace(this); + } +} + +// marking an error object as operational (true) +throw new AppError(errorManagement.commonErrors.InvalidInput, 'Describe here what happened', true); + +``` +
    + +### Blog Quote: "Programmer errors are bugs in the program" + +From the blog, Joyent ranked 1 for the keywords “Node.js error handling” + + > …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… + +### Blog Quote: "No safe way to leave without creating some undefined brittle state" + +From Node.js official documentation + + > …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker. + +### Blog Quote: "Otherwise you risk the state of your application" + +From the blog, debugable.com ranked 3 for the keywords “Node.js uncaught exception” + + > …So, unless you really know what you are doing, you should perform a graceful restart of your service after receiving an “uncaughtException” exception event. Otherwise, you risk the state of your application, or that of 3rd party libraries to become inconsistent, leading to all kinds of crazy bugs… + +### Blog Quote: "There are three schools of thoughts on error handling" + +From the blog: JS Recipes + +> …There are primarily three schools of thoughts on error handling: +1. Let the application crash and restart it. +2. Handle all possible errors and never crash. +3. A balanced approach between the two diff --git a/sections/errorhandling/shuttingtheprocess.japanese.md b/sections/errorhandling/shuttingtheprocess.japanese.md new file mode 100644 index 000000000..e7f8b9ee3 --- /dev/null +++ b/sections/errorhandling/shuttingtheprocess.japanese.md @@ -0,0 +1,99 @@ +# Exit the process gracefully when a stranger comes to town + +### One Paragraph Explainer + +Somewhere within your code, an error handler object is responsible for deciding how to proceed when an error is thrown – if the error is trusted (i.e. operational error, see further explanation within best practice #3) then writing to log file might be enough. Things get hairy if the error is not familiar – this means that some component might be in a faulty state and all future requests are subject to failure. For example, assuming a singleton, stateful token issuer service that threw an exception and lost its state – from now it might behave unexpectedly and cause all requests to fail. Under this scenario, kill the process and use a ‘Restarter tool’ (like Forever, PM2, etc) to start over with a clean state. + +### Code example: deciding whether to crash + +
    +Javascript + +```javascript +// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 +process.on('uncaughtException', (error) => { + errorManagement.handler.handleError(error); + if(!errorManagement.handler.isTrustedError(error)) + process.exit(1) +}); + +// centralized error handler encapsulates error-handling related logic +function errorHandler() { + this.handleError = (error) => { + return logger.logError(error) + .then(sendMailToAdminIfCritical) + .then(saveInOpsQueueIfCritical) + .then(determineIfOperationalError); + } + + this.isTrustedError = (error) => { + return error.isOperational; + } +} +``` +
    + +
    +Typescript + +```typescript +// Assuming developers mark known operational errors with error.isOperational=true, read best practice #3 +process.on('uncaughtException', (error: Error) => { + errorManagement.handler.handleError(error); + if(!errorManagement.handler.isTrustedError(error)) + process.exit(1) +}); + +// centralized error object that derives from Node’s Error +export class AppError extends Error { + public readonly isOperational: boolean; + + constructor(description: string, isOperational: boolean) { + super(description); + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + this.isOperational = isOperational; + Error.captureStackTrace(this); + } +} + +// centralized error handler encapsulates error-handling related logic +class ErrorHandler { + public async handleError(err: Error): Promise { + await logger.logError(err); + await sendMailToAdminIfCritical(); + await saveInOpsQueueIfCritical(); + await determineIfOperationalError(); + }; + + public isTrustedError(error: Error) { + if (error instanceof AppError) { + return error.isOperational; + } + return false; + } +} + +export const handler = new ErrorHandler(); +``` +
    + +### Blog Quote: "The best way is to crash" + +From the blog Joyent + +> …The best way to recover from programmer errors is to crash immediately. You should run your programs using a restarter that will automatically restart the program in the event of a crash. With a restarter in place, crashing is the fastest way to restore reliable service in the face of a transient programmer error… + +### Blog Quote: "There are three schools of thoughts on error handling" + +From the blog: JS Recipes + +> …There are primarily three schools of thoughts on error handling: +1. Let the application crash and restart it. +2. Handle all possible errors and never crash. +3. A balanced approach between the two + +### Blog Quote: "No safe way to leave without creating some undefined brittle state" + +From Node.js official documentation + +> …By the very nature of how throw works in JavaScript, there is almost never any way to safely “pick up where you left off”, without leaking references, or creating some other sort of undefined brittle state. The safest way to respond to a thrown error is to shut down the process. Of course, in a normal web server, you might have many connections open, and it is not reasonable to abruptly shut those down because an error was triggered by someone else. The better approach is to send an error response to the request that triggered the error while letting the others finish in their normal time, and stop listening for new requests in that worker. diff --git a/sections/errorhandling/testingerrorflows.japanese.md b/sections/errorhandling/testingerrorflows.japanese.md new file mode 100644 index 000000000..552ca1f53 --- /dev/null +++ b/sections/errorhandling/testingerrorflows.japanese.md @@ -0,0 +1,81 @@ +# Test error flows using your favorite test framework + +### One Paragraph Explainer + +Testing ‘happy’ paths is no better than testing failures. Good testing code coverage demands to test exceptional paths. Otherwise, there is no trust that exceptions are indeed handled correctly. Every unit testing framework, like [Mocha](https://mochajs.org/) & [Chai](http://chaijs.com/), supports exception testing (code examples below). If you find it tedious to test every inner function and exception you may settle with testing only REST API HTTP errors. + +### Code example: ensuring the right exception is thrown using Mocha & Chai + +
    +Javascript + +```javascript +describe('Facebook chat', () => { + it('Notifies on new chat message', () => { + const chatService = new chatService(); + chatService.participants = getDisconnectedParticipants(); + expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); + }); +}); +``` +
    + +
    +Typescript + +```typescript +describe('Facebook chat', () => { + it('Notifies on new chat message', () => { + const chatService = new chatService(); + chatService.participants = getDisconnectedParticipants(); + expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); + }); +}); +``` +
    + +### Code example: ensuring API returns the right HTTP error code + +
    +Javascript + +```javascript +it('Creates new Facebook group', () => { + const invalidGroupInfo = {}; + return httpRequest({ + method: 'POST', + uri: 'facebook.com/api/groups', + resolveWithFullResponse: true, + body: invalidGroupInfo, + json: true + }).then((response) => { + expect.fail('if we were to execute the code in this block, no error was thrown in the operation above') + }).catch((response) => { + expect(400).to.equal(response.statusCode); + }); +}); +``` +
    + +
    +Typescript + +```typescript +it('Creates new Facebook group', async () => { + let invalidGroupInfo = {}; + try { + const response = await httpRequest({ + method: 'POST', + uri: 'facebook.com/api/groups', + resolveWithFullResponse: true, + body: invalidGroupInfo, + json: true + }) + // if we were to execute the code in this block, no error was thrown in the operation above + expect.fail('The request should have failed') + } catch(response) { + expect(400).to.equal(response.statusCode); + } +}); +``` +
    \ No newline at end of file diff --git a/sections/errorhandling/usematurelogger.japanese.md b/sections/errorhandling/usematurelogger.japanese.md new file mode 100644 index 000000000..237de5309 --- /dev/null +++ b/sections/errorhandling/usematurelogger.japanese.md @@ -0,0 +1,50 @@ +# Use a mature logger to increase errors visibility + +### One Paragraph Explainer + +We all love console.log but obviously, a reputable and persistent logger like [Winston][winston] (highly popular) or [Pino][pino] (the new kid in town which is focused on performance) is mandatory for serious projects. A set of practices and tools will help to reason about errors much quicker – (1) log frequently using different levels (debug, info, error), (2) when logging, provide contextual information as JSON objects, see example below. (3) Watch and filter logs using a log querying API (built-in in most loggers) or a log viewer software. (4) Expose and curate log statement for the operation team using operational intelligence tools like Splunk. + +[winston]: https://www.npmjs.com/package/winston +[pino]: https://www.npmjs.com/package/pino + +### Code Example – Winston Logger in action + +```javascript +// your centralized logger object +const logger = new winston.Logger({ + level: 'info', + transports: [ + new (winston.transports.Console)() + ] +}); + +// custom code somewhere using the logger +logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); +``` + +### Code Example – Querying the log folder (searching for entries) + +```javascript +const options = { + from: Date.now() - 24 * 60 * 60 * 1000, + until: new Date(), + limit: 10, + start: 0, + order: 'desc', + fields: ['message'] +}; + +// Find items logged between today and yesterday. +winston.query(options, (err, results) => { + // execute callback with results +}); +``` + +### Blog Quote: "Logger Requirements" + + From the blog Strong Loop + +> Lets identify a few requirements (for a logger): +1. Timestamp each log line. This one is pretty self-explanatory – you should be able to tell when each log entry occurred. +2. Logging format should be easily digestible by humans as well as machines. +3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time… diff --git a/sections/errorhandling/useonlythebuiltinerror.japanese.md b/sections/errorhandling/useonlythebuiltinerror.japanese.md new file mode 100644 index 000000000..36bdec01d --- /dev/null +++ b/sections/errorhandling/useonlythebuiltinerror.japanese.md @@ -0,0 +1,117 @@ +# Use only the built-in Error object + +### One Paragraph Explainer + +The permissive nature of JavaScript along with its variety of code-flow options (e.g. EventEmitter, Callbacks, Promises, etc) pushes to great variance in how developers raise errors – some use strings, other define their own custom types. Using Node.js built-in Error object helps to keep uniformity within your code and with 3rd party libraries, it also preserves significant information like the StackTrace. When raising the exception, it’s usually a good practice to fill it with additional contextual properties like the error name and the associated HTTP error code. To achieve this uniformity and practices, consider extending the Error object with additional properties, but be careful not to overdo it. It's generally a good idea to extend the built-in Error object only once with an AppError for all the application level errors, and pass any data you need to differentiate between different kinds of errors as arguments. No need to extend the Error object multiple times (one for each error case, such as DbError, HttpError) See code examples below + +### Code Example – doing it right + +```javascript +// throwing an Error from typical function, whether sync or async +if(!productToAdd) + throw new Error('How can I add new product when no value provided?'); + +// 'throwing' an Error from EventEmitter +const myEmitter = new MyEmitter(); +myEmitter.emit('error', new Error('whoops!')); + +// 'throwing' an Error from a Promise +const addProduct = async (productToAdd) => { + try { + const existingProduct = await DAL.getProduct(productToAdd.id); + if (existingProduct !== null) { + throw new Error('Product already exists!'); + } + } catch (err) { + // ... + } +} +``` + +### Code example – Anti Pattern + +```javascript +// throwing a string lacks any stack trace information and other important data properties +if(!productToAdd) + throw ('How can I add new product when no value provided?'); +``` + +### Code example – doing it even better + +
    +Javascript + +```javascript +// centralized error object that derives from Node’s Error +function AppError(name, httpCode, description, isOperational) { + Error.call(this); + Error.captureStackTrace(this); + this.name = name; + //...other properties assigned here +}; + +AppError.prototype = Object.create(Error.prototype); +AppError.prototype.constructor = AppError; + +module.exports.AppError = AppError; + +// client throwing an exception +if(user == null) + throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true) +``` +
    + +
    +Typescript + +```typescript +// centralized error object that derives from Node’s Error +export class AppError extends Error { + public readonly name: string; + public readonly httpCode: HttpCode; + public readonly isOperational: boolean; + + constructor(name: string, httpCode: HttpCode, description: string, isOperational: boolean) { + super(description); + + Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain + + this.name = name; + this.httpCode = httpCode; + this.isOperational = isOperational; + + Error.captureStackTrace(this); + } +} + +// client throwing an exception +if(user == null) + throw new AppError(commonErrors.resourceNotFound, commonHTTPErrors.notFound, 'further explanation', true) +``` +
    + +*Explanation about the `Object.setPrototypeOf` in Typescript: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-newtarget* + +### Blog Quote: "I don’t see the value in having lots of different types" + +From the blog, Ben Nadel ranked 5 for the keywords “Node.js error object” + +>…”Personally, I don’t see the value in having lots of different types of error objects [in contrast with having only one] – JavaScript, as a language, doesn’t seem to cater to Constructor-based error-catching. As such, differentiating on an object property seems far easier than differentiating on a Constructor type… + +### Blog Quote: "A string is not an error" + +From the blog, devthought.com ranked 6 for the keywords “Node.js error object” + +> …passing a string instead of an error results in reduced interoperability between modules. It breaks contracts with APIs that might be performing `instanceof` Error checks, or that want to know more about the error. Error objects, as we’ll see, have very interesting properties in modern JavaScript engines besides holding the message passed to the constructor… + +### Blog Quote: "Inheriting from Error doesn’t add too much value" + +From the blog machadogj + +> …One problem that I have with the Error class is that is not so simple to extend. Of course, you can inherit the class and create your own Error classes like HttpError, DbError, etc. However, that takes time and doesn’t add too much value [compared to extending it only once for an AppError] unless you are doing something with types. Sometimes, you just want to add a message and keep the inner error, and sometimes you might want to extend the error with parameters, and such… + +### Blog Quote: "All JavaScript and System errors raised by Node.js inherit from Error" + +From Node.js official documentation + +> …All JavaScript and System errors raised by Node.js inherit from, or are instances of, the standard JavaScript Error class and are guaranteed to provide at least the properties available on that class. A generic JavaScript Error object that does not denote any specific circumstance of why the error occurred. Error objects capture a “stack trace” detailing the point in the code at which the Error was instantiated, and may provide a text description of the error. All errors generated by Node.js, including all System and JavaScript errors, will either be instances of or inherit from, the Error class… diff --git a/sections/performance/block-loop.japanese.md b/sections/performance/block-loop.japanese.md new file mode 100644 index 000000000..531de7fef --- /dev/null +++ b/sections/performance/block-loop.japanese.md @@ -0,0 +1,50 @@ +# Don't block the event loop + +

    + +Node handles the Event Loop mostly on a single thread rotating through multiple queues. Operations with high complexity, large json parsing, applying logic over huge arrays, unsafe regex queries, and large IO operations are some of the operations that can cause the Event Loop to stall. Avoid this off-loading CPU intensive tasks to a dedicated service (e.g. job server), or breaking long tasks into small steps then using the Worker Pool are some examples of how to avoid blocking the Event Loop. + +### Example: blocking the event loop +Let's take a look at an example from [Node Clinic](https://clinicjs.org/documentation/doctor/05-fixing-event-loop-problem). +```javascript +function sleep (ms) { + const future = Date.now() + ms + while (Date.now() < future); +} + +server.get('/', (req, res, next) => { + sleep(30) + res.send({}) + next() +}) +``` + +And when we benchmark this app, we start to see the latency caused by the long +while loop. + +### Run the benchmark +`clinic doctor --on-port 'autocannon localhost:$PORT' -- node slow-event-loop` + +### The results + +``` +─────────┬────────┬────────┬────────┬────────┬───────────┬──────────┬───────────┐ +│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │ +├─────────┼────────┼────────┼────────┼────────┼───────────┼──────────┼───────────┤ +│ Latency │ 270 ms │ 300 ms │ 328 ms │ 331 ms │ 300.56 ms │ 38.55 ms │ 577.05 ms │ +└─────────┴────────┴────────┴────────┴────────┴───────────┴──────────┴───────────┘ +┌───────────┬─────────┬─────────┬─────────┬────────┬─────────┬───────┬─────────┐ +│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │ +├───────────┼─────────┼─────────┼─────────┼────────┼─────────┼───────┼─────────┤ +│ Req/Sec │ 31 │ 31 │ 33 │ 34 │ 32.71 │ 1.01 │ 31 │ +├───────────┼─────────┼─────────┼─────────┼────────┼─────────┼───────┼─────────┤ +``` + +## Image of the Event Loop +![Event Loop](/assets/images/event-loop.png "Event Loop") + +>Here's a good rule of thumb for keeping your Node server speedy: Node is fast when the work associated with each client at any given time is "small". +>[Don't Block the Event Loop (or the Worker Pool) | Node.js](https://nodejs.org/en/docs/guides/dont-block-the-event-loop/) + +> Most people fail their first few NodeJS apps merely due to the lack of understanding of the concepts such as the Event Loop, Error handling and asynchrony +[Event Loop Best Practices — NodeJS Event Loop Part 5](https://jsblog.insiderattack.net/event-loop-best-practices-nodejs-event-loop-part-5-e29b2b50bfe2) diff --git a/sections/performance/nativeoverutil.japanese.md b/sections/performance/nativeoverutil.japanese.md new file mode 100644 index 000000000..48804fe99 --- /dev/null +++ b/sections/performance/nativeoverutil.japanese.md @@ -0,0 +1,69 @@ +# Prefer native JS methods over user-land utils like Lodash + + +

    + +### One Paragraph Explainer +Sometimes, using native methods is better than requiring _lodash_ or _underscore_ because those libraries can lead to performance loss or take up more space than needed +The performance using native methods result in an [overall ~50% gain](https://github.com/Berkmann18/NativeVsUtils/blob/master/analysis.xlsx) which includes the following methods: `Array.concat`, `Array.fill`, `Array.filter`, `Array.map`, `(Array|String).indexOf`, `Object.find`, ... + + + + +

    + +### Example: benchmark comparison - Lodash vs V8 (Native) +The graph below shows the [mean of the benchmarks for a variety of Lodash methods](https://github.com/Berkmann18/NativeVsUtils/blob/master/nativeVsLodash.ods), this shows that Lodash methods take on average 146.23% more time to complete the same tasks as V8 methods. + +![meanDiag](../../assets/images/sampleMeanDiag.png) + +### Code Example – Benchmark test on `_.concat`/`Array.concat` +```javascript +const _ = require('lodash'); +const __ = require('underscore'); +const Suite = require('benchmark').Suite; +const opts = require('./utils'); //cf. https://github.com/Berkmann18/NativeVsUtils/blob/master/utils.js + +const concatSuite = new Suite('concat', opts); +const array = [0, 1, 2]; + +concatSuite.add('lodash', () => _.concat(array, 3, 4, 5)) + .add('underscore', () => __.concat(array, 3, 4, 5)) + .add('native', () => array.concat(3, 4, 5)) + .run({ 'async': true }); +``` + +Which returns this: + +![output](../../assets/images/concat-benchmark.png) + +You can find a bigger list of benchmarks [here](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.txt) or alternatively [run this](https://github.com/Berkmann18/NativeVsUtils/blob/master/index.js) which would show the same but with colours. + +### Blog Quote: "You don't (may not) need Lodash/Underscore" + +From the [repo on this matter which focuses on Lodash and Underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore). + + > Lodash and Underscore are great modern JavaScript utility libraries, and they are widely used by Front-end developers. However, when you are targeting modern browsers, you may find out that there are many methods which are already supported natively thanks to ECMAScript5 [ES5] and ECMAScript2015 [ES6]. If you want your project to require fewer dependencies, and you know your target browser clearly, then you may not need Lodash/Underscore. + +### Example: Linting for non-native methods usage +There's an [ESLint plugin](https://www.npmjs.com/package/eslint-plugin-you-dont-need-lodash-underscore) which detects where you're using libraries but don't need to by warning you with suggestions (cf. example below).
    +The way you set it up is by adding the `eslint-plugin-you-dont-need-lodash-underscore` plugin to your ESLint configuration file: +```json +{ + "extends": [ + "plugin:you-dont-need-lodash-underscore/compatible" + ] +} +``` + +### Example: detecting non-v8 util usage using a linter +Consider the file below: +```js +const _ = require('lodash'); +// ESLint will flag the line above with a suggestion +console.log(_.map([0, 1, 2, 4, 8, 16], x => `d${x}`)); +``` +Here's what ESLint would output when using the YDNLU plugin. +![output](../../assets/images/ydnlu.png) + +Of course, the example above doesn't seem realistic considering what actual codebases would have but you get the idea. diff --git a/sections/production/LTSrelease.japanese.md b/sections/production/LTSrelease.japanese.md new file mode 100644 index 000000000..de2194066 --- /dev/null +++ b/sections/production/LTSrelease.japanese.md @@ -0,0 +1,20 @@ +# Use an LTS release of Node.js in production + +### One Paragraph Explainer + +Ensure you are using an LTS(Long Term Support) version of Node.js in production to receive critical bug fixes, security updates and performance improvements. + +LTS versions of Node.js are supported for at least 18 months and are indicated by even version numbers (e.g. 4, 6, 8). They're best for production since the LTS release line is focussed on stability and security, whereas the 'Current' release line has a shorter lifespan and more frequent updates to the code. Changes to LTS versions are limited to bug fixes for stability, security updates, possible npm updates, documentation updates and certain performance improvements that can be demonstrated to not break existing applications. + +

    + +### Read on + +🔗 [Node.js release definitions](https://nodejs.org/en/about/releases/) + +🔗 [Node.js release schedule](https://github.com/nodejs/Release) + +🔗 [Essential Steps: Long Term Support for Node.js by Rod Vagg](https://medium.com/@nodesource/essential-steps-long-term-support-for-node-js-8ecf7514dbd) +> ...the schedule of incremental releases within each of these will be driven by the availability of bug fixes, security fixes, and other small but important changes. The focus will be on stability, but stability also includes minimizing the number of known bugs and staying on top of security concerns as they arise. + +

    diff --git a/sections/production/apmproducts.japanese.md b/sections/production/apmproducts.japanese.md new file mode 100644 index 000000000..bd7e5754b --- /dev/null +++ b/sections/production/apmproducts.japanese.md @@ -0,0 +1,25 @@ +# Sure user experience with APM products + +

    + +### One Paragraph Explainer + +APM (application performance monitoring) refers to a family of products that aims to monitor application performance from end to end, also from the customer perspective. While traditional monitoring solutions focus on Exceptions and standalone technical metrics (e.g. error tracking, slow server endpoints, etc), in the real world our app might create disappointed users without any code exceptions, for example, if some middleware service performed real slow. APM products measure the user experience from end to end, for example, given a system that encompasses frontend UI and multiple distributed services – some APM products can tell how fast a transaction that spans multiple tiers last. It can tell whether the user experience is solid and point to the problem. This attractive offering comes with a relatively high price tag hence it’s recommended for large-scale and complex products that require going beyond straightforward monitoring. + +

    + +### APM example – a commercial product that visualizes cross-service app performance + +![APM example](/assets/images/apm1.png "APM example") + +

    + +### APM example – a commercial product that emphasizes the user experience score + +![APM example](/assets/images/apm2.png "APM example") + +

    + +### APM example – a commercial product that highlights slow code paths + +![APM example](/assets/images/apm3.png "APM example") diff --git a/sections/production/assigntransactionid.japanese.md b/sections/production/assigntransactionid.japanese.md new file mode 100644 index 000000000..90abfd7a3 --- /dev/null +++ b/sections/production/assigntransactionid.japanese.md @@ -0,0 +1,39 @@ +# Assign ‘TransactionId’ to each log statement + +

    + +### One Paragraph Explainer + +A typical log is a warehouse of entries from all components and requests. Upon detection of some suspicious line or error, it becomes hairy to match other lines that belong to the same specific flow (e.g. the user “John” tried to buy something). This becomes even more critical and challenging in a microservice environment when a request/transaction might span across multiple computers. Address this by assigning a unique transaction identifier value to all the entries from the same request so when detecting one line one can copy the id and search for every line that has similar transaction id. However, achieving this In Node is not straightforward as a single thread is used to serve all requests –consider using a library that that can group data on the request level – see code example on the next slide. When calling other microservices, pass the transaction id using an HTTP header like “x-transaction-id” to keep the same context. + +

    + +### Code example: typical Express configuration + +```javascript +// when receiving a new request, start a new isolated context and set a transaction id. The following example is using the npm library continuation-local-storage to isolate requests + +const { createNamespace } = require('continuation-local-storage'); +const session = createNamespace('my session'); + +router.get('/:id', (req, res, next) => { + session.set('transactionId', 'some unique GUID'); + someService.getById(req.params.id); + logger.info('Starting now to get something by id'); +}); + +// Now any other service or components can have access to the contextual, per-request, data +class someService { + getById(id) { + logger.info('Starting to get something by id'); + // other logic comes here + } +} + +// The logger can now append the transaction id to each entry so that entries from the same request will have the same value +class logger { + info (message) { + console.log(`${message} ${session.get('transactionId')}`); + } +} +``` diff --git a/sections/production/bestateless.japanese.md b/sections/production/bestateless.japanese.md new file mode 100644 index 000000000..f7242a687 --- /dev/null +++ b/sections/production/bestateless.japanese.md @@ -0,0 +1,42 @@ +# Be stateless, kill your Servers almost every day + +

    + +### One Paragraph Explainer + +Have you ever encountered a severe production issue where one server was missing some piece of configuration or data? That is probably due to some unnecessary dependency on some local asset that is not part of the deployment. Many successful products treat servers like a phoenix bird – it dies and is reborn periodically without any damage. In other words, a server is just a piece of hardware that executes your code for some time and is replaced after that. +This approach + +- allows scaling by adding and removing servers dynamically without any side-effects. +- simplifies the maintenance as it frees our mind from evaluating each server state. + +

    + +### Code example: anti-patterns + +```javascript +// Typical mistake 1: saving uploaded files locally on a server +const multer = require('multer'); // express middleware for handling multipart uploads +const upload = multer({ dest: 'uploads/' }); + +app.post('/photos/upload', upload.array('photos', 12), (req, res, next) => {}); + +// Typical mistake 2: storing authentication sessions (passport) in a local file or memory +const FileStore = require('session-file-store')(session); +app.use(session({ + store: new FileStore(options), + secret: 'keyboard cat' +})); + +// Typical mistake 3: storing information on the global object +Global.someCacheLike.result = { somedata }; +``` + +

    + +### What Other Bloggers Say + +From the blog [Martin Fowler](https://martinfowler.com/bliki/PhoenixServer.html): +> ...One day I had this fantasy of starting a certification service for operations. The certification assessment would consist of a colleague and I turning up at the corporate data center and setting about critical production servers with a baseball bat, a chainsaw, and a water pistol. The assessment would be based on how long it would take for the operations team to get all the applications up and running again. This may be a daft fantasy, but there’s a nugget of wisdom here. While you should forego the baseball bats, it is a good idea to virtually burn down your servers at regular intervals. A server should be like a phoenix, regularly rising from the ashes... + +

    diff --git a/sections/production/createmaintenanceendpoint.japanese.md b/sections/production/createmaintenanceendpoint.japanese.md new file mode 100644 index 000000000..2db04a8b6 --- /dev/null +++ b/sections/production/createmaintenanceendpoint.japanese.md @@ -0,0 +1,45 @@ +# Create a maintenance endpoint + +

    + +### One Paragraph Explainer + +A maintenance endpoint is a highly secure HTTP API that is part of the app code and its purpose is to be used by the ops/production team to monitor and expose maintenance functionality. For example, it can return a heap dump (memory snapshot) of the process, report whether there are some memory leaks and even allow to execute REPL commands directly. This endpoint is needed where the conventional DevOps tools (monitoring products, logs, etc) fail to gather some specific type of information or you choose not to buy/install such tools. The golden rule is using professional and external tools for monitoring and maintaining the production, these are usually more robust and accurate. That said, there are likely to be cases where the generic tools will fail to extract information that is specific to Node or to your app – for example, should you wish to generate a memory snapshot at the moment GC completed a cycle – few npm libraries will be glad to perform this for you but popular monitoring tools will likely miss this functionality. It is important to keep this endpoint private and accessibly only by admins because it can become a target of a DDOS attack. + +

    + +### Code example: generating a heap dump via code + +```javascript +const heapdump = require('heapdump'); + +// Check if request is authorized +function isAuthorized(req) { + // ... +} + +router.get('/ops/heapdump', (req, res, next) => { + if (!isAuthorized(req)) { + return res.status(403).send('You are not authorized!'); + } + + logger.info('About to generate heapdump'); + + heapdump.writeSnapshot((err, filename) => { + console.log('heapdump file is ready to be sent to the caller', filename); + fs.readFile(filename, 'utf-8', (err, data) => { + res.end(data); + }); + }); +}); +``` + +

    + +### Recommended Resources + +[Getting your Node.js app production ready (Slides)](http://naugtur.pl/pres3/node2prod) + +▶ [Getting your Node.js app production ready (Video)](https://www.youtube.com/watch?v=lUsNne-_VIk) + +![Getting your Node.js app production ready](/assets/images/createmaintenanceendpoint1.png "Getting your Node.js app production ready") diff --git a/sections/production/delegatetoproxy.japanese.md b/sections/production/delegatetoproxy.japanese.md new file mode 100644 index 000000000..9c31d7e59 --- /dev/null +++ b/sections/production/delegatetoproxy.japanese.md @@ -0,0 +1,51 @@ +# Delegate anything possible (e.g. static content, gzip) to a reverse proxy + +

    + +### One Paragraph Explainer + +It’s very tempting to cargo-cult Express and use its rich middleware offering for networking related tasks like serving static files, gzip encoding, throttling requests, SSL termination, etc. This is a performance kill due to its single threaded model which will keep the CPU busy for long periods (Remember, Node’s execution model is optimized for short tasks or async IO related tasks). A better approach is to use a tool that expertise in networking tasks – the most popular are nginx and HAproxy which are also used by the biggest cloud vendors to lighten the incoming load on node.js processes. + +

    + +### Nginx Config Example – Using nginx to compress server responses + +```nginx +# configure gzip compression +gzip on; +gzip_comp_level 6; +gzip_vary on; + +# configure upstream +upstream myApplication { + server 127.0.0.1:3000; + server 127.0.0.1:3001; + keepalive 64; +} + +#defining web server +server { + # configure server with ssl and error pages + listen 80; + listen 443 ssl; + ssl_certificate /some/location/sillyfacesociety.com.bundle.crt; + error_page 502 /errors/502.html; + + # handling static content + location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { + root /usr/local/silly_face_society/node/public; + access_log off; + expires max; +} +``` + +

    + +### What Other Bloggers Say + +* From the blog [Mubaloo](http://mubaloo.com/best-practices-deploying-node-js-applications): +> …It’s very easy to fall into this trap – You see a package like Express and think “Awesome! Let’s get started” – you code away and you’ve got an application that does what you want. This is excellent and, to be honest, you’ve won a lot of the battle. However, you will lose the war if you upload your app to a server and have it listen on your HTTP port because you’ve forgotten a very crucial thing: Node is not a web server. **As soon as any volume of traffic starts to hit your application, you’ll notice that things start to go wrong: connections are dropped, assets stop being served or, at the very worst, your server crashes. What you’re doing is attempting to have Node deal with all of the complicated things that a proven web server does really well. Why reinvent the wheel?** +> **This is just for one request, for one image and bearing in mind this is the memory that your application could be used for important stuff like reading a database or handling complicated logic; why would you cripple your application for the sake of convenience?** + +* From the blog [Argteam](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> Although express.js has built-in static file handling through some connect middleware, you should never use it. **Nginx can do a much better job of handling static files and can prevent requests for non-dynamic content from clogging our node processes**… diff --git a/sections/production/detectvulnerabilities.japanese.md b/sections/production/detectvulnerabilities.japanese.md new file mode 100644 index 000000000..d67b9e819 --- /dev/null +++ b/sections/production/detectvulnerabilities.japanese.md @@ -0,0 +1,20 @@ +# Use tools that automatically detect vulnerable dependencies + +

    + +### One Paragraph Explainer + +Modern Node applications have tens and sometimes hundreds of dependencies. If any of the dependencies +you use has a known security vulnerability your app is vulnerable as well. +The following tools automatically check for known security vulnerabilities in your dependencies: + +- [npm audit](https://docs.npmjs.com/cli/audit) - npm audit +- [snyk](https://snyk.io/) - Continuously find & fix vulnerabilities in your dependencies + +

    + +### What Other Bloggers Say + +From the [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-one-security/) blog: + +> ...Using to manage your application’s dependencies is powerful and convenient. But the packages that you use may contain critical security vulnerabilities that could also affect your application. The security of your app is only as strong as the “weakest link” in your dependencies. Fortunately, there are two helpful tools you can use to ensure the third-party packages you use: nsp and requireSafe. These two tools do largely the same thing, so using both might be overkill, but “better safe than sorry” are words to live by when it comes to security... diff --git a/sections/production/frontendout.japanese.md b/sections/production/frontendout.japanese.md new file mode 100644 index 000000000..148bd344d --- /dev/null +++ b/sections/production/frontendout.japanese.md @@ -0,0 +1,45 @@ +# Get your frontend assets out of Node + +

    + +### One Paragraph Explainer + +In a classic web app the backend serves the frontend/graphics to the browser, a very common approach in the Node’s world is to use Express static middleware for streamlining static files to the client. BUT – Node is not a typical webapp as it utilizes a single thread that is not optimized to serve many files at once. Instead, consider using a reverse proxy (e.g. nginx, HAProxy), cloud storage or CDN (e.g. AWS S3, Azure Blob Storage, etc) that utilizes many optimizations for this task and gain much better throughput. For example, specialized middleware like nginx embodies direct hooks between the file system and the network card and uses a multi-threaded approach to minimize intervention among multiple requests. + +Your optimal solution might wear one of the following forms: + +1. Using a reverse proxy – your static files will be located right next to your Node application, only requests to the static files folder will be served by a proxy that sits in front of your Node app such as nginx. Using this approach, your Node app is responsible deploying the static files but not to serve them. Your frontend’s colleague will love this approach as it prevents cross-origin-requests from the frontend. + +2. Cloud storage – your static files will NOT be part of your Node app content, they will be uploaded to services like AWS S3, Azure BlobStorage, or other similar services that were born for this mission. Using this approach, your Node app is not responsible deploying the static files neither to serve them, hence a complete decoupling is drawn between Node and the Frontend which is anyway handled by different teams. + +

    + +### Configuration example: typical nginx configuration for serving static files + +```nginx +# configure gzip compression +gzip on; +keepalive 64; + +# defining web server +server { +listen 80; +listen 443 ssl; + +# handle static content +location ~ ^/(images/|img/|javascript/|js/|css/|stylesheets/|flash/|media/|static/|robots.txt|humans.txt|favicon.ico) { +root /usr/local/silly_face_society/node/public; +access_log off; +expires max; +} +``` + +

    + +### What Other Bloggers Say + +From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/): + +>…In development, you can use [res.sendFile()](http://expressjs.com/4x/api.html#res.sendFile) to serve static files. But don’t do this in production, because this function has to read from the file system for every file request, so it will encounter significant latency and affect the overall performance of the app. Note that res.sendFile() is not implemented with the sendfile system call, which would make it far more efficient. Instead, use serve-static middleware (or something equivalent), that is optimized for serving files for Express apps. An even better option is to use a reverse proxy to serve static files; see Use a reverse proxy for more information… + +

    diff --git a/sections/production/guardprocess.japanese.md b/sections/production/guardprocess.japanese.md new file mode 100644 index 000000000..719e85a73 --- /dev/null +++ b/sections/production/guardprocess.japanese.md @@ -0,0 +1,17 @@ +# Guard and restart your process upon failure (using the right tool) + +

    + +### One Paragraph Explainer + +At the base level, Node processes must be guarded and restarted upon failures. Simply put, for small apps and those who don’t use containers – tools like [PM2](https://www.npmjs.com/package/pm2-docker) are perfect as they bring simplicity, restarting capabilities and also rich integration with Node. Others with strong Linux skills might use systemd and run Node as a service. Things get more interesting for apps that use Docker or any container technology since those are usually accompanied by cluster management and orchestration tools (e.g. [AWS ECS](http://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html), [Kubernetes](https://kubernetes.io/), etc) that deploy, monitor and heal containers. Having all those rich cluster management features including container restart, why mess up with other tools like PM2? There’s no bulletproof answer. There are good reasons to keep PM2 within containers (mostly its containers specific version [pm2-docker](https://www.npmjs.com/package/pm2-docker)) as the first guarding tier – it’s much faster to restart a process and provide Node-specific features like flagging to the code when the hosting container asks to gracefully restart. Other might choose to avoid unnecessary layers. To conclude this write-up, no solution suits them all and getting to know the options is the important thing + +

    + +### What Other Bloggers Say + +* From the [Express Production Best Practices](https://expressjs.com/en/advanced/best-practice-performance.html): +> ... In development, you started your app simply from the command line with node server.js or something similar. **But doing this in production is a recipe for disaster. If the app crashes, it will be offline** until you restart it. To ensure your app restarts if it crashes, use a process manager. A process manager is a “container” for applications that facilitate deployment, provides high availability, and enables you to manage the application at runtime. + +* From the Medium blog post [Understanding Node Clustering](https://medium.com/@CodeAndBiscuits/understanding-nodejs-clustering-in-docker-land-64ce2306afef#.cssigr5z3): +> ... Understanding Node.js Clustering in Docker-Land “Docker containers are streamlined, lightweight virtual environments, designed to simplify processes to their bare minimum. Processes that manage and coordinate their own resources are no longer as valuable. **Instead, management stacks like Kubernetes, Mesos, and Cattle have popularized the concept that these resources should be managed infrastructure-wide**. CPU and memory resources are allocated by “schedulers”, and network resources are managed by stack-provided load balancers. diff --git a/sections/production/installpackageswithnpmci.japanese.md b/sections/production/installpackageswithnpmci.japanese.md new file mode 100644 index 000000000..ed7e497ce --- /dev/null +++ b/sections/production/installpackageswithnpmci.japanese.md @@ -0,0 +1,30 @@ +# Install packages with npm ci in production + +

    + +### One Paragraph Explainer + +You locked your dependencies following [**Lock dependencies**](/sections/production/lockdependencies.md) but you now need to make sure those exact package versions are used in production. + +Using `npm ci` to install packages will do exactly that and more. +* It will fail if your `package.json` and your `package-lock.json` do not match (they should) or if you don't have a lock file +* If a `node_modules` folder is present it will automatically remove it before installing +* It is faster! Nearly twice as fast according to [the release blog post](https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable) + +### When can this be useful? +You are guaranteed that you CI environment or QA will test your software with exactly the same package version that the one you will later send to production. +Also, if for some reason someone manually changes package.json, not through a cli command but rather by directly editing package.json, a gap between package.json & package-lock.json is created and an error will be thrown. + +### What npm says + +From [npm ci documentation](https://docs.npmjs.com/cli/ci.html) +> This command is similar to npm-install, except it’s meant to be used in automated environments such as test platforms, continuous integration, and deployment – or any situation where you want to make sure you’re doing a clean install of your dependencies. + +[Blog post announcing the release of `ci` command](https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable) +> The command offers massive improvements to both the performance and reliability of builds for continuous integration / continuous deployment processes, providing a consistent and fast experience for developers using CI/CD in their workflow. + +[npmjs: dependencies and devDepencies](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file) +> "dependencies": Packages required by your application in production. +> "devDependencies": Packages that are only needed for local development and testing. + +

    diff --git a/sections/production/lockdependencies.japanese.md b/sections/production/lockdependencies.japanese.md new file mode 100644 index 000000000..1698c9bbb --- /dev/null +++ b/sections/production/lockdependencies.japanese.md @@ -0,0 +1,69 @@ +# Lock dependencies + +

    + +### One Paragraph Explainer + +Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production npm might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using npm config files and the argument ```–save-exact=true``` instructs npm to refer to the *exact* same version that was installed so the next time you run ```npm install``` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative and popular approach is using a `.shrinkwrap` file (easily generated using npm) that states exactly which packages and versions should be installed so no environment can get tempted to fetch newer versions than expected. + +* **Update:** as of npm 5, dependencies are locked automatically using .shrinkwrap. Yarn, an emerging package manager, also locks down dependencies by default. + +

    + +### Code example: .npmrc file that instructs npm to use exact versions + +```npmrc +// save this as .npmrc file on the project directory +save-exact:true +``` + +

    + +### Code example: shrinkwrap.json file that distills the exact dependency tree + +```json +{ + "name": "A", + "dependencies": { + "B": { + "version": "0.0.1", + "dependencies": { + "C": { + "version": "0.1.0" + } + } + } + } +} +``` + +

    + +### Code example: npm 5 dependencies lock file – package-lock.json + +```json +{ + "name": "package-name", + "version": "1.0.0", + "lockfileVersion": 1, + "dependencies": { + "cacache": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", + "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" + }, + "duplexify": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", + "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" + } + } + } + } +} +``` diff --git a/sections/production/logrouting.japanese.md b/sections/production/logrouting.japanese.md new file mode 100644 index 000000000..9fac3f52a --- /dev/null +++ b/sections/production/logrouting.japanese.md @@ -0,0 +1,87 @@ +# Your application code should not handle log routing + +

    + +### One Paragraph Explainer + +Application code should not handle log routing, but instead should use a logger utility to write to `stdout/stderr`. “Log routing” means picking up and pushing logs to a some other location than your application or application process, for example, writing the logs to a file, database, etc. The reason for this is mostly two-fold: 1) separation of concerns and 2) [12-Factor best practices for modern applications](https://12factor.net/logs). + +We often think of "separation of concerns" in terms of pieces of code between services and between services themselves, but this applies to the more “infrastructural” components as well. Your application code should not handle something that should be handled by infrastructure/the execution environment (most often these days, containers). What happens if you define the log locations in your application, but later you need to change that location? That results in a code change and deployment. When working with container-based/cloud-based platforms, containers can spin up and shut down when scaling to performance demands, so we can't be sure where a logfile will end up. The execution environment (container) should decide where the log files get routed to instead. The application should just log what it needs to to `stdout` / `stderr`, and the execution environment should be configured to pick up the log stream from there and route it to where it needs to go. Also, those on the team who need to specify and/or change the log destinations are often not application developers but are part of DevOps, and they might not have familiarity with the application code. This prevents them from easily making changes. + +

    + +### Code Example – Anti-pattern: Log routing tightly coupled to application + +```javascript +const { createLogger, transports, winston } = require('winston'); +/** + * Requiring `winston-mongodb` will expose + * `winston.transports.MongoDB` + */ +require('winston-mongodb'); + +// log to two different files, which the application now must be concerned with +const logger = createLogger({ + transports: [ + new transports.File({ filename: 'combined.log' }), + ], + exceptionHandlers: [ + new transports.File({ filename: 'exceptions.log' }) + ] +}); + +// log to MongoDB, which the application now must be concerned with +winston.add(winston.transports.MongoDB, options); +``` +Doing it this way, the application now handles both application/business logic AND log routing logic! + +

    + +### Code Example – Better log handling + Docker example +In the application: +```javascript +const logger = new winston.Logger({ + level: 'info', + transports: [ + new (winston.transports.Console)() + ] +}); + +logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' }); +``` +Then, in the docker container `daemon.json`: +```json5 +{ + "log-driver": "splunk", // just using Splunk as an example, it could be another storage type + "log-opts": { + "splunk-token": "", + "splunk-url": "", + //... + } +} +``` +So this example ends up looking like `log -> stdout -> Docker container -> Splunk` + +

    + +### Blog Quote: "O'Reilly" + +From the [O'Reilly blog](https://www.oreilly.com/ideas/a-cloud-native-approach-to-logs), + > When you have a fixed number of instances on a fixed number of servers, storing logs on disk seems to make sense. However, when your application can dynamically go from 1 running instance to 100, and you have no idea where those instances are running, you need your cloud provider to deal with aggregating those logs on your behalf. + +

    + +### Quote: "12-Factor" + +From the [12-Factor best practices for logging](https://12factor.net/logs), + > A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout. + + > In staging or production deploys, each process’ stream will be captured by the execution environment, collated together with all other streams from the app, and routed to one or more final destinations for viewing and long-term archival. These archival destinations are not visible to or configurable by the app, and instead are completely managed by the execution environment. + +

    + + ### Example: Architecture overview using Docker and Splunk as an example + +![alt text](/assets/images/logging-overview.png "Log routing overview") + +

    diff --git a/sections/production/measurememory.japanese.md b/sections/production/measurememory.japanese.md new file mode 100644 index 000000000..3b5b31d21 --- /dev/null +++ b/sections/production/measurememory.japanese.md @@ -0,0 +1,25 @@ +# Measure and guard the memory usage + +

    + +### One Paragraph Explainer + +In a perfect world, a web developer shouldn’t deal with memory leaks. In reality, memory issues are a known Node’s gotcha one must be aware of. Above all, memory usage must be monitored constantly. In the development and small production sites, you may gauge manually using Linux commands or npm tools and libraries like node-inspector and memwatch. The main drawback of this manual activities is that they require a human being actively monitoring – for serious production sites, it’s absolutely vital to use robust monitoring tools e.g. (AWS CloudWatch, DataDog or any similar proactive system) that alerts when a leak happens. There are also few development guidelines to prevent leaks: avoid storing data on the global level, use streams for data with dynamic size, limit variables scope using let and const. + +

    + +### What Other Bloggers Say + +* From the blog [Dyntrace](http://apmblog.dynatrace.com/): +> ... ”As we already learned, in Node.js JavaScript is compiled to native code by V8. The resulting native data structures don’t have much to do with their original representation and are solely managed by V8. This means that we cannot actively allocate or deallocate memory in JavaScript. V8 uses a well-known mechanism called garbage collection to address this problem.” + +* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> ... “Although this example leads to obvious results the process is always the same: +Create heap dumps with some time and a fair amount of memory allocation in between +Compare a few dumps to find out what’s growing” + +* From the blog [Dyntrace](http://blog.argteam.com/coding/hardening-node-js-for-production-part-2-using-nginx-to-avoid-node-js-load): +> ... “fault, Node.js will try to use about 1.5GBs of memory, which has to be capped when running on systems with less memory. This is the expected behavior as garbage collection is a very costly operation. +The solution for it was adding an extra parameter to the Node.js process: +node –max_old_space_size=400 server.js –production ” +“Why is garbage collection expensive? The V8 JavaScript engine employs a stop-the-world garbage collector mechanism. In practice, it means that the program stops execution while garbage collection is in progress.” diff --git a/sections/production/monitoring.japanese.md b/sections/production/monitoring.japanese.md new file mode 100644 index 000000000..75b0f753e --- /dev/null +++ b/sections/production/monitoring.japanese.md @@ -0,0 +1,39 @@ +# Monitoring! + +

    + +### One Paragraph Explainer + +At the very basic level, monitoring means you can *easily* identify when bad things happen at production. For example, by getting notified by email or Slack. The challenge is to choose the right set of tools that will satisfy your requirements without breaking your bank. May I suggest, start with defining the core set of metrics that must be watched to ensure a healthy state – CPU, server RAM, Node process RAM (less than 1.4GB), the number of errors in the last minute, number of process restarts, average response time. Then go over some advanced features you might fancy and add to your wish list. Some examples of a luxury monitoring feature: DB profiling, cross-service measuring (i.e. measure business transaction), front-end integration, expose raw data to custom BI clients, Slack notifications and many others. + +Achieving the advanced features demands lengthy setup or buying a commercial product such as Datadog, NewRelic and alike. Unfortunately, achieving even the basics is not a walk in the park as some metrics are hardware-related (CPU) and others live within the node process (internal errors) thus all the straightforward tools require some additional setup. For example, cloud vendor monitoring solutions (e.g. [AWS CloudWatch](https://aws.amazon.com/cloudwatch/), [Google StackDriver](https://cloud.google.com/stackdriver/)) will tell you immediately about the hardware metrics but not about the internal app behavior. On the other end, Log-based solutions such as ElasticSearch lack the hardware view by default. The solution is to augment your choice with missing metrics, for example, a popular choice is sending application logs to [Elastic stack](https://www.elastic.co/products) and configure some additional agent (e.g. [Beat](https://www.elastic.co/products)) to share hardware-related information to get the full picture. + +

    + +### Monitoring example: AWS cloudwatch default dashboard. Hard to extract in-app metrics + +![AWS cloudwatch default dashboard. Hard to extract in-app metrics](/assets/images/monitoring1.png) + +

    + +### Monitoring example: StackDriver default dashboard. Hard to extract in-app metrics + +![StackDriver default dashboard. Hard to extract in-app metrics](/assets/images/monitoring2.jpg) + +

    + +### Monitoring example: Grafana as the UI layer that visualizes raw data + +![Grafana as the UI layer that visualizes raw data](/assets/images/monitoring3.png) + +

    + +### What Other Bloggers Say + +From the blog [Rising Stack](http://mubaloo.com/best-practices-deploying-node-js-applications/): + +> …We recommend you to watch these signals for all of your services: +> Error Rate: Because errors are user facing and immediately affect your customers. +> Response time: Because the latency directly affects your customers and business. +> Throughput: The traffic helps you to understand the context of increased error rates and the latency too. +> Saturation: It tells how “full” your service is. If the CPU usage is 90%, can your system handle more traffic? … diff --git a/sections/production/productioncode.japanese.md b/sections/production/productioncode.japanese.md new file mode 100644 index 000000000..a86047312 --- /dev/null +++ b/sections/production/productioncode.japanese.md @@ -0,0 +1,16 @@ +# Make your code production-ready + +

    + +### One Paragraph Explainer + +Following is a list of development tips that greatly affect the production maintenance and stability: + +* The twelve-factor guide – Get familiar with the [Twelve factors](https://12factor.net/) guide +* Be stateless – Save no data locally on a specific web server (see separate bullet – ‘Be Stateless’) +* Cache – Utilize cache heavily, yet never fail because of cache mismatch +* Test memory – gauge memory usage and leaks as part your development flow, tools such as ‘memwatch’ can greatly facilitate this task +* Name functions – Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will provide memory usage per method name +* Use CI tools – Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference errors and undefined variables. Use –trace-sync-io to identify code that uses synchronous APIs (instead of the async version) +* Log wisely – Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such as Elastic can search upon those properties (see separate bullet – ‘Increase visibility using smart logs’). Also, include transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate bullet – ‘Include Transaction-ID’) +* Error management – Error handling is the Achilles’ heel of Node.js production sites – many Node processes are crashing because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy is absolutely critical, read here my [error handling best practices](http://goldbergyoni.com/checklist-best-practices-of-node-js-error-handling/) diff --git a/sections/production/setnodeenv.japanese.md b/sections/production/setnodeenv.japanese.md new file mode 100644 index 000000000..4c721fec4 --- /dev/null +++ b/sections/production/setnodeenv.japanese.md @@ -0,0 +1,34 @@ +# Set NODE_ENV = production + +

    + +### One Paragraph Explainer + +Process environment variables is a set of key-value pairs made available to any running program, usually for configuration purposes. Though any variables can be used, Node encourages the convention of using a variable called NODE_ENV to flag whether we’re in production right now. This determination allows components to provide better diagnostics during development, for example by disabling caching or emitting verbose log statements. Any modern deployment tool – Chef, Puppet, CloudFormation, others – support setting environment variables during deployment + +

    + +### Code example: Setting and reading the NODE_ENV environment variable + +```shell script +// Setting environment variables in bash before starting the node process +$ NODE_ENV=development +$ node +``` + +```javascript +// Reading the environment variable using code +if (process.env.NODE_ENV === 'production') + useCaching = true; +``` + +

    + +### What Other Bloggers Say + +From the blog [dynatrace](https://www.dynatrace.com/blog/the-drastic-effects-of-omitting-node_env-in-your-express-js-applications/): +> ...In Node.js there is a convention to use a variable called NODE_ENV to set the current mode. We see that it, in fact, reads NODE_ENV and defaults to ‘development’ if it isn’t set. We clearly see that by setting NODE_ENV to production the number of requests Node.js can handle jumps by around two-thirds while the CPU usage even drops slightly. *Let me emphasize this: Setting NODE_ENV to production makes your application 3 times faster!* + +![NODE_ENV=production](/assets/images/setnodeenv1.png "NODE_ENV=production") + +

    diff --git a/sections/production/smartlogging.japanese.md b/sections/production/smartlogging.japanese.md new file mode 100644 index 000000000..5a8161e3e --- /dev/null +++ b/sections/production/smartlogging.japanese.md @@ -0,0 +1,40 @@ +# Make your app transparent using smart logs + +

    + +### One Paragraph Explainer + +Since you print out log statements anyway and you're obviously in a need of some interface that wraps up production information where you can trace errors and core metrics (e.g. how many errors happen every hour and which is your slowest API end-point) why not invest some moderate effort in a robust logging framework that will tick all boxes? Achieving that requires a thoughtful decision on three steps: + +**1. smart logging** – at the bare minimum you need to use a reputable logging library like [Winston](https://github.com/winstonjs/winston), [Bunyan](https://github.com/trentm/node-bunyan) and write meaningful information at each transaction start and end. Consider to also format log statements as JSON and provide all the contextual properties (e.g. user id, operation type, etc) so that the operations team can act on those fields. Include also a unique transaction ID at each log line, for more information refer to the bullet below “Write transaction-id to log”. One last point to consider is also including an agent that logs the system resource like memory and CPU like Elastic Beat. + +**2. smart aggregation** – once you have comprehensive information on your server's file system, it’s time to periodically push these to a system that aggregates, facilitates and visualizes this data. The Elastic stack, for example, is a popular and free choice that offers all the components to aggregate and visualize data. Many commercial products provide similar functionality only they greatly cut down the setup time and require no hosting. + +**3. smart visualization** – now the information is aggregated and searchable, one can be satisfied only with the power of easily searching the logs but this can go much further without coding or spending much effort. We can now show important operational metrics like error rate, average CPU throughout the day, how many new users opted-in in the last hour and any other metric that helps to govern and improve our app. + +

    + +### Visualization Example: Kibana (part of the Elastic stack) facilitates advanced searching on log content + +![Kibana facilitates advanced searching on log content](/assets/images/smartlogging1.png "Kibana facilitates advanced searching on log content") + +

    + +### Visualization Example: Kibana (part of the Elastic stack) visualizes data based on logs + +![Kibana visualizes data based on logs](/assets/images/smartlogging2.jpg "Kibana visualizes data based on logs") + +

    + +### Blog Quote: Logger Requirements + +From the blog [Strong Loop](https://strongloop.com/strongblog/compare-node-js-logging-winston-bunyan/): + +> Lets identify a few requirements (for a logger): +> 1. Timestamp each log line. This one is pretty self-explanatory – you should be able to tell when each log entry occurred. +> 2. Logging format should be easily digestible by humans as well as machines. +> 3. Allows for multiple configurable destination streams. For example, you might be writing trace logs to one file but when an error is encountered, write to the same file, then into error file and send an email at the same time… + +

    + +

    diff --git a/sections/production/utilizecpu.japanese.md b/sections/production/utilizecpu.japanese.md new file mode 100644 index 000000000..301a0fc78 --- /dev/null +++ b/sections/production/utilizecpu.japanese.md @@ -0,0 +1,26 @@ +# Utilize all CPU cores + +

    + +### One Paragraph Explainer + +It might not come as a surprise that in its basic form, Node runs over a single thread=single process=single CPU. Paying for beefy hardware with 4 or 8 CPU and utilizing only one sounds crazy, right? The quickest solution which fits medium sized apps is using Node’s Cluster module which in 10 lines of code spawns a process for each logical core and route requests between the processes in a round-robin style. Even better, use PM2 which sugarcoats the clustering module with a simple interface and cool monitoring UI. While this solution works well for traditional applications, it might fall short for applications that require top-notch performance and robust DevOps flow. For those advanced use cases, consider replicating the NODE process using custom deployment script and balancing using a specialized tool such as nginx or use a container engine such as AWS ECS or Kubernetees that have advanced features for deployment and replication of processes. + +

    + +### Comparison: Balancing using Node’s cluster vs nginx + +![Balancing using Node’s cluster vs nginx](/assets/images/utilizecpucores1.png "Balancing using Node’s cluster vs nginx") + +

    + +### What Other Bloggers Say + +* From the [Node.js documentation](https://nodejs.org/api/cluster.html#cluster_how_it_works): +> ... The second approach, Node clusters, should, in theory, give the best performance. In practice, however, distribution tends to be very unbalanced due to operating system scheduler vagaries. Loads have been observed where over 70% of all connections ended up in just two processes, out of a total of eight ... + +* From the blog [StrongLoop](https://strongloop.com/strongblog/best-practices-for-express-in-production-part-two-performance-and-reliability/): +> ... Clustering is made possible with Node’s cluster module. This enables a master process to spawn worker processes and distribute incoming connections among the workers. However, rather than using this module directly, it’s far better to use one of the many tools out there that do it for you automatically; for example node-pm or cluster-service ... + +* From the Medium post [Node.js process load balance performance: comparing cluster module, iptables, and Nginx](https://medium.com/@fermads/node-js-process-load-balancing-comparing-cluster-iptables-and-nginx-6746aaf38272) +> ... Node cluster is simple to implement and configure, things are kept inside Node’s realm without depending on other software. Just remember your master process will work almost as much as your worker processes and with a little less request rate than the other solutions ... diff --git a/sections/projectstructre/breakintcomponents.japanese.md b/sections/projectstructre/breakintcomponents.japanese.md index 2190132c1..69cf5761c 100644 --- a/sections/projectstructre/breakintcomponents.japanese.md +++ b/sections/projectstructre/breakintcomponents.japanese.md @@ -1,84 +1,37 @@ - - -# コンポーネントによってソリューションを構築する - -

    - - - -### 概要 - - - - 中規模以上のアプリケーションにおいて、モノリスな構造はよくありません。多くの依存関係を持つ1つの大きなソフトウェアを保持することは、考えるのが難しいだけでなく、しばしばスパゲッティコードに陥ります。賢い設計者――すなわち獣を飼い慣らして、それを「モジュール化」するのに十分なスキルを持っている人――でさえ、設計に多大な精神的な努力を費やし、変更ごとに他の依存オブジェクトへの影響を慎重に評価する必要があります。究極の解決策は、小さなソフトウェアを開発することです。スタック全体を他のファイルと共有しない自己完結型のコンポーネントに分割し、それぞれが非常に少数のファイル(API、サービス、データアクセス、テストなど)によって構成されるようにします。このやり方であれば考えるのは簡単です。これは「マイクロサービス」アーキテクチャとも呼ばれています。マイクロサービスはあなたが従わなければならない仕様ではなく、むしろ一連の原則であることを理解することが重要です。あなたは本格的なマイクロサービスアーキテクチャに多くの原則を採用しても、またはいくつかのみを採用するかもしたとしても、ソフトウェアの複雑さを低く抑える限り、どちらでも問題はありません。最低限必要なのは、コンポーネント間に基本的な境界線を作成し、各ビジネスコンポーネントにプロジェクトルートのフォルダを割り当ててそれを自己完結型にすることです。他のコンポーネントは、そのパブリックインタフェースまたはAPIを通じてのみ機能を使用できます。これは、コンポーネントをシンプルに保ち、依存性地獄を避け、アプリケーションが成長したら将来的に本格的なマイクロサービスへの道を開くための基盤です。 - -

    - - - -### ブログからの引用:”スケーリングにはアプリケーション全体のスケーリングが必要となる” - - - - MartinFowler.comより引用 - - - -> モノリシックアプリケーションはうまくいくこともありますが、より多くのアプリケーションがクラウドにデプロイされるようになるにつれて、人々はますます不満を感じてきています。変更サイクルは密接に関連しています。アプリケーションのごく一部に変更を加えるにも、モノリス全体を再構築してデプロイする必要があります。時間が経つにつれて、良いモジュール構造を維持するのは難しいことが多く、そのモジュール内の1つのモジュールだけに影響を与えるべき変更を維持するのが難しくなります。スケーリングは、リソースの追加を必要とするアプリケーションの一部だけではなく、アプリケーション全体でのスケーリングを必要とします。 - -

    - - - -### ブログより引用:”それでは、アプリケーションのアーキテクチャーは何を叫ぶのでしょうか。” - - - -ブログ [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html) より引用 - - - - ->  図書館の建築(アーキテクチャ)を見ている時、そこには壮大な入り口、チェックインカウンター、読書エリア、小さな会議室、そして、その図書館のすべての本を収納できる本棚をが配置された空間があるでしょう。その建築は叫ぶでしょう、図書館だと。
    - - それでは、アプリケーションのアーキテクチャは何を叫ぶのでしょうか。トップレベルのディレクトリ構造と、最上位パッケージのソースファイルを見たとき、彼らが叫ぶのは、ヘルスケアシステム? 会計システム? または在庫管理システム? それとも、彼らはRails、Spring/Hibernate、それともASPと叫ぶのでしょうか? - -

    - - - -### GOOD:自己完結型のコンポーネントでソリューションを構築する - -![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") - -

    - - - -### BAD:技術的な役割別でファイルをグループ化してしまう - -![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") +# Structure your solution by components + +

    + +### One Paragraph Explainer + +For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows. + +

    + +### Blog Quote: "Scaling requires scaling of the entire application" + + From the blog MartinFowler.com + +> Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource. + +

    + +### Blog Quote: "So what does the architecture of your application scream?" + + From the blog [uncle-bob](https://8thlight.com/blog/uncle-bob/2011/09/30/Screaming-Architecture.html) + +> ...if you were looking at the architecture of a library, you’d likely see a grand entrance, an area for check-in-out clerks, reading areas, small conference rooms, and gallery after gallery capable of holding bookshelves for all the books in the library. That architecture would scream: Library.
    + +So what does the architecture of your application scream? When you look at the top level directory structure, and the source files in the highest level package; do they scream: Health Care System, or Accounting System, or Inventory Management System? Or do they scream: Rails, or Spring/Hibernate, or ASP?. + +

    + +### Good: Structure your solution by self-contained components + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") + +

    + +### Bad: Group your files by technical role + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/configguide.japanese.md b/sections/projectstructre/configguide.japanese.md new file mode 100644 index 000000000..81d7918a0 --- /dev/null +++ b/sections/projectstructre/configguide.japanese.md @@ -0,0 +1,43 @@ +# Use environment aware, secure and hierarchical config + +

    + +### One Paragraph Explainer + +When dealing with configuration data, many things can just annoy and slow down: + +1. setting all the keys using process environment variables becomes very tedious when in need to inject 100 keys (instead of just committing those in a config file), however when dealing with files only the DevOps admins cannot alter the behavior without changing the code. A reliable config solution must combine both configuration files + overrides from the process variables + +2. when specifying all keys in a flat JSON, it becomes frustrating to find and modify entries when the list grows bigger. A hierarchical JSON file that is grouped into sections can overcome this issue + few config libraries allow to store the configuration in multiple files and take care to union all at runtime. See example below + +3. storing sensitive information like DB password is obviously not recommended but no quick and handy solution exists for this challenge. Some configuration libraries allow to encrypt files, others encrypt those entries during GIT commits or simply don't store real values for those entries and specify the actual value during deployment via environment variables. + +4. some advanced configuration scenarios demand to inject configuration values via command line (vargs) or sync configuration info via a centralized cache like Redis so multiple servers will use the same configuration data. + +5. the application should fail as fast as possible and provide the immediate feedback if the required environment variables are not present at start-up, this can be achieved by using [convict](https://www.npmjs.com/package/convict) to validate the configuration. + +Some configuration libraries can provide most of these features for free, have a look at npm libraries like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict) which tick many of these requirements. + +

    + +### Code Example – hierarchical config helps to find entries and maintain huge config files + +```json5 +{ + // Customer module configs + "Customer": { + "dbConfig": { + "host": "localhost", + "port": 5984, + "dbName": "customers" + }, + "credit": { + "initialLimit": 100, + // Set low for development + "initialDays": 1 + } + } +} +``` + +

    diff --git a/sections/projectstructre/createlayers.japanese.md b/sections/projectstructre/createlayers.japanese.md new file mode 100644 index 000000000..f6f860c12 --- /dev/null +++ b/sections/projectstructre/createlayers.japanese.md @@ -0,0 +1,13 @@ +# Layer your app, keep Express within its boundaries + +

    + + ### Separate component code into layers: web, services, and DAL + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Separate component code into layers") + +

    + +### 1 min explainer: The downside of mixing layers + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/keepexpressinweb.gif "The downside of mixing layers") diff --git a/sections/projectstructre/separateexpress.japanese.md b/sections/projectstructre/separateexpress.japanese.md new file mode 100644 index 000000000..e35c67d24 --- /dev/null +++ b/sections/projectstructre/separateexpress.japanese.md @@ -0,0 +1,100 @@ +# Separate Express 'app' and 'server' + +

    + +### One Paragraph Explainer + +The latest Express generator comes with a great practice that is worth to keep - the API declaration is separated from the network related configuration (port, protocol, etc). This allows testing the API in-process, without performing network calls, with all the benefits that it brings to the table: fast testing execution and getting coverage metrics of the code. It also allows deploying the same API under flexible and different network conditions. Bonus: better separation of concerns and cleaner code + +

    + +### Code example: API declaration, should reside in app.js/app.ts + +```javascript +const app = express(); +app.use(bodyParser.json()); +app.use('/api/events', events.API); +app.use('/api/forms', forms); +``` + +### Code example: Server network declaration, should reside in /bin/www + +
    +Javascript + +```javascript +const app = require('../app'); +const http = require('http'); + +// Get port from environment and store in Express. +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +// Create HTTP server. +const server = http.createServer(app); +``` +
    + +
    +Typescript + +```typescript +import app from '../app'; +import http from 'http'; + +// Get port from environment and store in Express. +const port = normalizePort(process.env.PORT || '3000'); +app.set('port', port); + +// Create HTTP server. +const server = http.createServer(app); +``` +
    + +### Example: test your API in-process using supertest (popular testing package) + +
    +Javascript + +```javascript +const request = require('supertest'); +const app = express(); + +app.get('/user', (req, res) => { + res.status(200).json({ name: 'tobi' }); +}); + +request(app) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end((err, res) => { + if (err) throw err; + }); +``` +
    + + +
    +Typescript + +```typescript +import * as request from "supertest"; +const app = express(); + +app.get('/user', (req: Request, res: Response) => { + res.status(200).json({ name: 'tobi' }); +}); + +request(app) + .get('/user') + .expect('Content-Type', /json/) + .expect('Content-Length', '15') + .expect(200) + .end((err: Error) => { + if (err) throw err; + }); + +``` +
    diff --git a/sections/projectstructre/thincomponents.japanese.md b/sections/projectstructre/thincomponents.japanese.md new file mode 100644 index 000000000..7d5535b6e --- /dev/null +++ b/sections/projectstructre/thincomponents.japanese.md @@ -0,0 +1,27 @@ +# Structure your solution by components + +

    + +### One Paragraph Explainer + +For medium sized apps and above, monoliths are really bad - one big software with many dependencies is just hard to reason about and often leads to code spaghetti. Even those smart architects who are skilled to tame the beast and 'modularize' it - spend great mental effort on design and each change requires to carefully evaluate the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc) so that it's very easy to reason about it. Some may call this 'microservices' architecture - it's important to understand that microservices are not a spec which you must follow rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependencies hell and pave the way to full-blown microservices in the future once your app grows + +

    + +### Blog Quote: "Scaling requires scaling of the entire application" + + From the blog MartinFowler.com + + > Monolithic applications can be successful, but increasingly people are feeling frustrations with them - especially as more applications are being deployed to the cloud. Change cycles are tied together - a change made to a small part of the application requires the entire monolith to be rebuilt and deployed. Over time it's often hard to keep a good modular structure, making it harder to keep changes that ought to only affect one module within that module. Scaling requires scaling of the entire application rather than parts of it that require greater resource. + +

    + +### Good: Structure your solution by self-contained components + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebycomponents.PNG "Structuring solution by components") + +

    + +### Bad: Group your files by technical role + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/structurebyroles.PNG "Structuring solution by technical roles") diff --git a/sections/projectstructre/wraputilities.japanese.md b/sections/projectstructre/wraputilities.japanese.md new file mode 100644 index 000000000..ed3639007 --- /dev/null +++ b/sections/projectstructre/wraputilities.japanese.md @@ -0,0 +1,13 @@ +# Wrap common utilities as npm packages + +

    + +### One Paragraph Explainer + +Once you start growing and have different components on different servers which consumes similar utilities, you should start managing the dependencies - how can you keep 1 copy of your utility code and let multiple consumer components use and deploy it? well, there is a tool for that, it's called npm... Start by wrapping 3rd party utility packages with your own code to make it easily replaceable in the future and publish your own code as private npm package. Now, all your code base can import that code and benefit free dependency management tool. It's possible to publish npm packages for your own private use without sharing it publicly using [private modules](https://docs.npmjs.com/private-modules/intro), [private registry](https://npme.npmjs.com/docs/tutorials/npm-enterprise-with-nexus.html) or [local npm packages](https://medium.com/@arnaudrinquin/build-modular-application-with-npm-local-modules-dfc5ff047bcc) + +

    + +### Sharing your own common utilities across environments and components + +![alt text](https://github.com/goldbergyoni/nodebestpractices/blob/master/assets/images/Privatenpm.png "Structuring solution by components") diff --git a/sections/security/avoid_publishing_secrets.japanese.md b/sections/security/avoid_publishing_secrets.japanese.md new file mode 100644 index 000000000..934325bb5 --- /dev/null +++ b/sections/security/avoid_publishing_secrets.japanese.md @@ -0,0 +1,44 @@ +# Avoid publishing secrets to the npm registry + +### One Paragraph Explainer +Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to blacklist specific files or folders, or the `files` array in `package.json` can act as a whitelist. + +To gain a view of what npm publish will really publish to the registry, the `--dry-run` flag can be added the npm publish command to provide a verbose view of the tarbell package created. + +It is important to note that if a project is utilising both `.npmignore` and `.gitignore` files, everything which isn't in `.npmignore` is published to the registry(i.e. the `.npmignore` file overrides the `.gitignore`). This condition is a common source of confusion and is a problem that can lead to leaking secrets. Developers may end up updating the `.gitignore` file, but forget to update `.npmignore` as well, which can lead to a potentially sensitive file not being pushed to source control, but still being included in the npm package. + +### Code example +Example .npmignore file +``` +#tests +test +coverage + +#build tools +.travis.yml +.jenkins.yml + +#environment +.env +.config + +``` + +Example use of files array in package.json + +``` +{ + "files" : [ + "dist/moment.js", + "dist/moment.min.js" + ] +} +``` + +### What other bloggers say + +From the blog by [Liran Tal & Juan Picado at Snyk](https://snyk.io/blog/ten-npm-security-best-practices/): +> ... Another good practice to adopt is making use of the files property in package.json, which works as a whitelist and specifies the array of files to be included in the package that is to be created and installed (while the ignore file functions as a blacklist). The files property and an ignore file can both be used together to determine which files should explicitly be included, as well as excluded, from the package. When using both, the former the files property in package.json takes precedence over the ignore file. + +From the [npm blog](https://blog.npmjs.org/post/165769683050/publishing-what-you-mean-to-publish) +> ... When you run npm publish, npm bundles up all the files in the current directory. It makes a few decisions for you about what to include and what to ignore. To make these decisions, it uses the contents of several files in your project directory. These files include .gitignore, .npmignore, and the files array in the package.json. It also always includes certain files and ignores others. \ No newline at end of file diff --git a/sections/security/avoideval.japanese.md b/sections/security/avoideval.japanese.md new file mode 100644 index 000000000..4afa0017d --- /dev/null +++ b/sections/security/avoideval.japanese.md @@ -0,0 +1,23 @@ +# Avoid JS eval statements + +### One Paragraph Explainer + +`eval()`, `setTimeout()`, `setInterval()`, and `new Function()` are global functions, often used in Node.js, which accept a string parameter representing a JavaScript expression, statement, or sequence of statements. The security concern of using these functions is the possibility that untrusted user input might find its way into code execution leading to server compromise, as evaluating user code essentially allows an attacker to perform any actions that you can. It is suggested to refactor code to not rely on the usage of these functions where user input could be passed to the function and executed. + +### Code example + +```javascript +// example of malicious code which an attacker was able to input +const userInput = "require('child_process').spawn('rm', ['-rf', '/'])"; + +// malicious code executed +eval(userInput); +``` + +### What other bloggers say + +From the Essential Node.js Security book by [Liran Tal](https://leanpub.com/nodejssecurity): +> The eval() function is perhaps of the most frowned upon JavaScript pieces from a security +perspective. It parses a JavaScript string as text, and executes it as if it were a JavaScript code. +Mixing that with untrusted user input that might find it’s way to eval() is a recipe for disaster that +can end up with server compromise. diff --git a/sections/security/bcryptpasswords.japanese.md b/sections/security/bcryptpasswords.japanese.md new file mode 100644 index 000000000..03a73a3ce --- /dev/null +++ b/sections/security/bcryptpasswords.japanese.md @@ -0,0 +1,32 @@ +# Avoid using the Node.js Crypto library for passwords, use Bcrypt + +### One Paragraph Explainer + +When storing user passwords, using an adaptive hashing algorithm such as bcrypt, offered by the [bcrypt npm module](https://www.npmjs.com/package/bcrypt) is recommended as opposed to using the native Node.js crypto module. `Math.random()` should also never be used as part of any password or token generation due to its predictability. + +The `bcrypt` module or similar should be used as opposed to the JavaScript implementation, as when using `bcrypt`, a number of 'rounds' can be specified in order to provide a secure hash. This sets the work factor or the number of 'rounds' the data is processed for, and more hashing rounds leads to more secure hash (although this at the cost of CPU time). The introduction of hashing rounds means that the brute force factor is significantly reduced, as password crackers are slowed down increasing the time required to generate one attempt. + +### Code example + +```javascript +try { +// asynchronously generate a secure password using 10 hashing rounds + const hash = await bcrypt.hash('myPassword', 10); + // Store secure hash in user record + + // compare a provided password input with saved hash + const match = await bcrypt.compare('somePassword', hash); + if (match) { + // Passwords match + } else { + // Passwords don't match + } +} catch { + logger.error('could not hash password.') +} +``` + +### What other bloggers say + +From the blog by [Max McCarty](https://dzone.com/articles/nodejs-and-password-storage-with-bcrypt): +> ... it’s not just using the right hashing algorithm. I’ve talked extensively about how the right tool also includes the necessary ingredient of “time” as part of the password hashing algorithm and what it means for the attacker who’s trying to crack passwords through brute-force. diff --git a/sections/security/childprocesses.japanese.md b/sections/security/childprocesses.japanese.md new file mode 100644 index 000000000..75bfccbb8 --- /dev/null +++ b/sections/security/childprocesses.japanese.md @@ -0,0 +1,31 @@ +# Be cautious when working with child processes + +### One Paragraph Explainer + +As great as child processes are, they should be used with caution. Passing in user input must be sanitized, if not avoided at all. +The dangers of unsanitized input executing system-level logic are unlimited, reaching from remote code execution to the exposure of +sensitive system data and even data loss. A check list of preparations could look like this + +- avoid user input in every case, otherwise validate and sanitize it +- limit the privileges of the parent and child processes using user/group identities +- run your process inside of an isolated environment to prevent unwanted side-effects if the other preparations fail + +### Code example: Dangers of unsanitized child process executions + +```javascript +const { exec } = require('child_process'); + +... + +// as an example, take a script that takes two arguments, one of them is unsanitized user input +exec('"/path/to/test file/someScript.sh" --someOption ' + input); + +// -> imagine what could happen if the user simply enters something like '&& rm -rf --no-preserve-root /' +// you'd be in for an unwanted surprise +``` + +### Additional resources + +From the Node.js child process [documentation](https://nodejs.org/dist/latest-v8.x/docs/api/child_process.html#child_process_child_process_exec_command_options_callback): + +> Never pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution. diff --git a/sections/security/commonsecuritybestpractices.japanese.md b/sections/security/commonsecuritybestpractices.japanese.md new file mode 100644 index 000000000..0e2e6f558 --- /dev/null +++ b/sections/security/commonsecuritybestpractices.japanese.md @@ -0,0 +1,131 @@ +[✔]: ../../assets/images/checkbox-small-blue.png + +# Common Node.js security best practices + +The common security guidelines section contains best practices that are standardized in many frameworks and conventions, running an application with SSL/TLS, for example, should be a common guideline and convention followed in every setup to achieve great security benefits. + +## ![✔] Use SSL/TLS to encrypt the client-server connection + +**TL;DR:** In the times of [free SSL/TLS certificates](https://letsencrypt.org/) and easy configuration of those, you do no longer have to weigh advantages and disadvantages of using a secure server because the advantages such as security, support of modern technology and trust clearly outweigh the disadvantages like minimal overhead compared to pure HTTP. + +**Otherwise:** Attackers could perform man-in-the-middle attacks, spy on your users' behaviour and perform even more malicious actions when the connection is unencrypted + +🔗 [**Read More: Running a secure Node.js server**](/sections/security/secureserver.md) + +

    + +## ![✔] Comparing secret values and hashes securely + +**TL;DR:** When comparing secret values or hashes like HMAC digests, you should use the [`crypto.timingSafeEqual(a, b)`](https://nodejs.org/dist/latest-v9.x/docs/api/crypto.html#crypto_crypto_timingsafeequal_a_b) function Node provides out of the box since Node.js v6.6.0. This method compares two given objects and keeps comparing even if data does not match. The default equality comparison methods would simply return after a character mismatch, allowing timing attacks based on the operation length. + +**Otherwise:** Using default equality comparison operators you might expose critical information based on the time taken to compare two objects + +

    + +## ![✔] Generating random strings using Node.js + +**TL;DR:** Using a custom-built function generating pseudo-random strings for tokens and other security-sensitive use cases might actually not be as random as you think, rendering your application vulnerable to cryptographic attacks. When you have to generate secure random strings, use the [`crypto.RandomBytes(size, [callback])`](https://nodejs.org/dist/latest-v9.x/docs/api/crypto.html#crypto_crypto_randombytes_size_callback) function using available entropy provided by the system. + +**Otherwise:** When generating pseudo-random strings without cryptographically secure methods, attackers might predict and reproduce the generated results, rendering your application insecure + +

    + +Going on, below we've listed some important bits of advice from the OWASP project. + +## ![✔] OWASP A2: Broken Authentication + +- Require MFA/2FA for important services and accounts +- Rotate passwords and access keys frequently, including SSH keys +- Apply strong password policies, both for ops and in-application user management ([🔗 OWASP password recommendation](https://www.owasp.org/index.php/Authentication_Cheat_Sheet#Implement_Proper_Password_Strength_Controls.22)) +- Do not ship or deploy your application with any default credentials, particularly for admin users or external services you depend on +- Use only standard authentication methods like OAuth, OpenID, etc.  - **avoid** basic authentication +- Auth rate limiting: Disallow more than _X_ login attempts (including password recovery, etc.) in a period of _Y_ +- On login failure, don't let the user know whether the username or password verification failed, just return a common auth error +- Consider using a centralized user management system to avoid managing multiple accounts per employee (e.g. GitHub, AWS, Jenkins, etc) and to benefit from a battle-tested user management system + +## ![✔] OWASP A5:  Broken access control + +- Respect the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege)  -  every component and DevOps person should only have access to the necessary information and resources +- **Never** work with the console/root (full-privilege) account except for account management +- Run all instances/containers on behalf of a role/service account +- Assign permissions to groups and not to users. This should make permission management easier and more transparent for most cases + +## ![✔] OWASP A6: Security Misconfiguration + +- Access to production environment internals is done through the internal network only, use SSH or other ways, but _never_ expose internal services +- Restrict internal network access  - explicitly set which resource can access other resources (e.g. network policy or subnets) +- If using cookies, configure it to "secured" mode where it's being sent over SSL only +- If using cookies, configure it for "same site" only so only requests from same domain will get back the designated cookies +- If using cookies, prefer "HttpOnly" configuration that prevent client-side JavaScript code from accessing the cookies +- Protect each VPC with strict and restrictive access rules +- Prioritize threats using any standard security threat modeling like STRIDE or DREAD +- Protect against DDoS attacks using HTTP(S) and TCP load balancers +- Perform periodic penetration tests by specialized agencies + +## ![✔] OWASP A3: Sensitive Data Exposure + +- Only accept SSL/TLS connections, enforce Strict-Transport-Security using headers +- Separate the network into segments (i.e. subnets) and ensure each node has the least necessary networking access permissions +- Group all services/instances that need no internet access and explicitly disallow any outgoing connection (a.k.a private subnet) +- Store all secrets in a vault products like AWS KMS, HashiCorp Vault or Google Cloud KMS +- Lockdown sensitive instance metadata using metadata +- Encrypt data in transit when it leaves a physical boundary +- Don't include secrets in log statements +- Avoid showing plain passwords in the frontend, take necessary measures in the backend and never store sensitive information in plaintext + +## ![✔] OWASP A9: Using Components With Known Security Vulneraibilities + +- Scan docker images for known vulnerabilities (using Docker's and other vendors' scanning services) +- Enable automatic instance (machine) patching and upgrades to avoid running old OS versions that lack security patches +- Provide the user with both 'id', 'access' and 'refresh' token so the access token is short-lived and renewed with the refresh token +- Log and audit each API call to cloud and management services (e.g who deleted the S3 bucket?) using services like AWS CloudTrail +- Run the security checker of your cloud provider (e.g. AWS security trust advisor) + + +## ![✔] OWASP A10: Insufficient Logging & Monitoring + +- Alert on remarkable or suspicious auditing events like user login, new user creation, permission change, etc +- Alert on irregular amount of login failures (or equivelant actions like forgot password) +- Include the time and username that initiated the update in each DB record + +## ![✔] OWASP A7: Cross-Site-Scripting (XSS) + +- Use templating engines or frameworks that automatically escape XSS by design, such as EJS, Pug, React, or Angular. Learn the limitations of each mechanisms XSS protection and appropriately handle the use cases which are not covered +- Escaping untrusted HTTP request data based on the context in the HTML output (body, attribute, JavaScript, CSS, or URL) will resolve Reflected and Stored XSS vulnerabilities +- Applying context-sensitive encoding when modifying the browser document on the client-side acts against DOM XSS +- Enabling a Content-Security Policy (CSP) as a defense-in-depth mitigating control against XSS + +## ![✔] Protect Personally Identifyable Information (PII Data) + +- Personally identifiable information (PII) is any data that can be used to identify a specific individual +- Protect Personally Identifyable Information in the Applications by encrypting them +- Follow the data privacy laws of the land + + +- Reference laws: + +- European Union: GDPR - https://ec.europa.eu/info/law/law-topic/data-protection_en +- India: https://meity.gov.in/writereaddata/files/Personal_Data_Protection_Bill,2018.pdf +- Singapore: https://www.pdpc.gov.sg/Legislation-and-Guidelines/Personal-Data-Protection-Act-Overview + +## ![✔] Have a security.txt File [PRODUCTION] + +**TL;DR:** Have a text file called ```security.txt``` under ```/.well-known``` directory (/.well-known/security.txt) or in the root directory (/security.txt) of your website or your web application in production. ```security.txt``` file should contain details using which security researchers can report vulnerabilities and also the contact details of the responsible person/group (email id and/or phone numbers) to whom the reports have to be sent. + +**Otherwise:** You may not be notified about the vulnerabilities. You will miss the opportunity to act on the vulnerabilities in time. + +🔗 [**Read More: security.txt**](https://securitytxt.org/) +


    + +## ![✔] Have a SECURITY.md File [OPEN SOURCE] + +**TL;DR:** To give people instructions for responsibly reporting security vulnerabilities in your project, you can add a SECURITY.md file to your repository's root, docs, or .github folder. SECURITY.md file should contain details using which security researchers can report vulnerabilities and also the contact details of the responsible person/group (email id and/or phone numbers) to whom the reports have to be sent. + +**Otherwise:** You may not be notified about the vulnerabilities. You will miss the opportunity to act on the vulnerabilities in time. + +🔗 [**Read More: SECURITY.md**](https://help.github.com/en/github/managing-security-vulnerabilities/adding-a-security-policy-to-your-repository) + +


    + + +


    diff --git a/sections/security/dependencysecurity.japanese.md b/sections/security/dependencysecurity.japanese.md new file mode 100644 index 000000000..33d52ad93 --- /dev/null +++ b/sections/security/dependencysecurity.japanese.md @@ -0,0 +1,53 @@ +# Constantly and automatically inspect for vulnerable dependencies + +### One Paragraph Explainer + +The majority of Node.js applications rely heavily on a large number of third party modules from npm or Yarn, both popular package registries, due to ease and speed of development. However, the downside to this benefit is the security risks of including unknown vulnerabilities into your application, which is a risk recognised by its place in the OWASP top critical web application security risks list. + +There is a number of tools available to help identify third-party packages in Node.js applications which have been identified as vulnerable by the community to mitigate the risk of introducing them into your project. These can be used periodically from CLI tools or included as part of your application's build process. + +### Table of Contents + +- [NPM audit](#npm-audit) +- [Snyk](#snyk) +- [Greenkeeper](#greenkeeper) + +### NPM Audit + +`npm audit` is a new cli tool introduced with NPM@6. + +Running `npm audit` will produce a report of security vulnerabilities with the affected package name, vulnerability severity and description, path, and other information, and, if available, commands to apply patches to resolve vulnerabilities. + +![npm audit example](/assets/images/npm-audit.png) + +🔗 [Read on: NPM blog](https://docs.npmjs.com/getting-started/running-a-security-audit) + +### Snyk + +Snyk offers a feature-rich CLI, as well as GitHub integration. Snyk goes further with this and in addition to notifying vulnerabilities, also automatically creates new pull requests fixing vulnerabilities as patches are released for known vulnerabilities. + +Snyk's feature rich website also allows for ad-hoc assessment of dependencies when provided with a GitHub repository or npm module url. You can also search for npm packages which have vulnerabilities directly. + +An example of the output of the Synk GitHub integration automatically created pull request: +![synk GitHub example](/assets/images/snyk.png) + +🔗 [Read on: Snyk website](https://snyk.io/) + +🔗 [Read on: Synk online tool to check npm packages and GitHub modules](https://snyk.io/test) + +### Greenkeeper + +Greenkeeper is a service which offers real-time dependency updates, which keeps an application more secure by always using the most up to date and patched dependency versions. + +Greenkeeper watches the npm dependencies specified in a repository's `package.json` file, and automatically creates a working branch with each dependency update. The repository CI suite is then run to reveal any breaking changes for the updated dependency version in the application. If CI fails due to the dependency update, a clear and concise issue is created in the repository to be auctioned, outlining the current and updated package versions, along with information and commit history of the updated version. + +An example of the output of the Greenkeeper GitHub integration automatically created pull request: + +![synk github example](/assets/images/greenkeeper.png) +🔗 [Read on: Greenkeeper website](https://greenkeeper.io/) + +### Additional resources + +🔗 [Rising Stack Blog: Node.js dependency risks](https://blog.risingstack.com/controlling-node-js-security-risk-npm-dependencies/) + +🔗 [NodeSource Blog: Improving npm security](https://nodesource.com/blog/how-to-reduce-risk-and-improve-security-around-npm) diff --git a/sections/security/escape-output.japanese.md b/sections/security/escape-output.japanese.md new file mode 100644 index 000000000..1f28acb45 --- /dev/null +++ b/sections/security/escape-output.japanese.md @@ -0,0 +1,62 @@ +# Escape Output + +### One Paragraph Explainer + +HTML and other web languages mix content with executable code - a single HTML paragraph might contain a visual representation of data along with JavaScript execution instructions. When rendering HTML or returning data from API, what we believe is a pure content might actually embody JavaScript code that will get interpreted and executed by the browser. This happens, for example, when we render content that was inserted by an attacker to a database - for example `
    `. This can be mitigated by instructing the browser to treat any chunk of untrusted data as content only and never interpret it - this technique is called escaping. Many npm libraries and HTML templating engines provide escaping capabilities (example: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi)). Not only HTML content should be escaped but also CSS and JavaScript + + +### Code example - Don't put untrusted data into your HTML + +```javascript + directly in a script + + inside an HTML comment + +
    in an attribute name + + in a tag name + + directly in CSS + +``` + +### Code example - Malicious content that might be injected into a DB + +```javascript +
    + A pseudo comment to the a post + +
    + +``` + +

    + +### Blog Quote: "When we don’t want the characters to be interpreted" + +From the Blog [benramsey.com](https://benramsey.com/articles/escape-output/) +> Data may leave your application in the form of HTML sent to a Web browser, SQL sent to a database, XML sent to an RSS reader, WML sent to a wireless device, etc. The possibilities are limitless. Each of these has its own set of special characters that are interpreted differently than the rest of the plain text received. Sometimes we want to send these special characters so that they are interpreted (HTML tags sent to a Web browser, for example), while other times (in the case of input from users or some other source), we don’t want the characters to be interpreted, so we need to escape them. + +> Escaping is also sometimes referred to as encoding. In short, it is the process of representing data in a way that it will not be executed or interpreted. For example, HTML will render the following text in a Web browser as bold-faced text because the tags have special meaning: +This is bold text. +But, suppose I want to render the tags in the browser and avoid their interpretation. Then, I need to escape the angle brackets, which have special meaning in HTML. The following illustrates the escaped HTML: + +<strong>This is bold text.</strong> + + +

    + +### Blog Quote: "OWASP recommends using a security-focused encoding library" + +From the blog OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "Writing these encoders is not tremendously difficult, but there are quite a few hidden pitfalls. For example, you might be tempted to use some of the escaping shortcuts like \" in JavaScript. However, these values are dangerous and may be misinterpreted by the nested parsers in the browser. You might also forget to escape the escape character, which attackers can use to neutralize your attempts to be safe. **OWASP recommends using a security-focused encoding library to make sure these rules are properly implemented**." + + +

    + +### Blog Quote: "You MUST use the escape syntax for the part of the HTML" + +From the blog OWASP [XSS (Cross Site Scripting) Prevention Cheat Sheet](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) +> "But HTML entity encoding doesn't work if you're putting untrusted data inside a
    `. This can be mitigated by instructing the browser to treat any chunk of untrusted data as content only and never interpret it - this technique is called escaping. Many npm libraries and HTML templating engines provide escaping capabilities (example: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi)). Not only HTML content should be escaped but also CSS and JavaScript +HTML やその他のウェブ言語は、コンテンツと実行可能なコードを混在させています。- 1 つの HTML の段落には、JavaScript を実行することによって実現されているデータの視覚的な表現が含まれている場合があります。HTML をレンダリングしたり API からデータを返したりするときに、純粋なコンテンツだと思っていたものが、実際にはブラウザによって解釈され、実行される JavaScript コードを含んでいる場合があります。これは例えば、攻撃者によってデータベースに挿入されたコンテンツをレンダリングするときに起こります - `
    ` といったように。これを緩和するには、ブラウザに対して、信頼されていないデータのかたまりをコンテンツとしてのみ扱い、決して解釈しないように指示することが有効です - このテクニックはエスケープと呼ばれます。多くの npm ライブラリや HTML テンプレートエンジンがエスケープ機能を提供しています (例: [escape-html](https://github.com/component/escape-html)、[node-esapi](https://github.com/ESAPI/node-esapi))。HTMLコンテンツだけでなく、CSS や JavaScript もエスケープする必要があります。 +### コード例 - 信頼されていないデータを HTML に挿入しない -### Code example - Don't put untrusted data into your HTML - -```javascript - directly in a script +```html + script タグの中に直接挿入する - inside an HTML comment + HTML コメントアウトの中に挿入する -
    in an attribute name +
    属性名として挿入する - in a tag name + <ここに信頼されていないデータを挿入しないでください... href="/test" /> 要素名として挿入する - directly in CSS + CSS に直接挿入する ``` -### Code example - Malicious content that might be injected into a DB +### コード例 - DB に注入される可能性のある悪質なコンテンツ -```javascript +```html
    A pseudo comment to the a post
    `. Hori arindu daiteke arakatzaileari aginduz konfiantzazko datuen zatiak edukitzat soilik tratatzeko eta inoiz ez interpretatzeko. Teknika horri ihes egitea deritzo. Npm liburutegi eta HTML txantiloi motor askok ihes egiteko baliabideak eskaintzen dituzte (adibidez: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi))). HTML edukiak ez ezik CSSk eta JavaScriptek ere ihes egin beharko lukete + + + +### Kode adibidea: ez jarri fidagarritasunik gabeko daturik zure HTMLan + +```javascript + zuzenean scriptean + + HTML komentario baten barruan + +
    ezaugarri izen batean + + tag izen batean + + CSSan zuzenean + +``` + +### Kode adibidea: datu base batean txerta daitekeen eduki kaltegarria + +```javascript +
    + Komentario bat + +
    + +``` + +

    + +### Blog aipua: "Pertsonaiak interpretatuak izatea nahi ez dugunean" + +[benramsey.com](https://benramsey.com/articles/escape-output/) bloga: +> Datuak modu askotara irten daitezke zure aplikaziotik: web nabigatzaile bati bidalitako HTML moduan, SQL datu basera bidalita, XML RSS irakurgailura bidalita, WML haririk gabeko gailu batera bidalita, etab. Aukerak mugagabeak dira. Horietako bakoitzak bere karaktere bereziak ditu, multzoka jasotzen dituena, eta jasotako gainerako testu arruntaren aldean desberdin interpretatzen dena. Batzuetan, karaktere berezi horiek bidali nahi ditugu interpretatuak izan ahal izateko (HTML nabigatzaile batera bidalitako HTML etiketak, adibidez); beste batzuetan (erabiltzaileek edo beste iturri batzuek egindako sarreren kasuan), ez dugu nahi karaktere horiek interpretatuak izan daitezen, eta, beraz, ihes egin behar diegu. + +> Ihes egiteari kodetzea ere esaten zaio batzuetan: ihes egitea edo kodetzea, laburbilduz,, datuak egikaritu edo interpretatuko ez diren moduan irudikatzeko prozesua da, alegia. Adibidez, HTMLk honako testu hau letra lodiz idatziko du web nabigatzaile batean etiketek esanahi berezia dutelako: +Testu hau letra lodiz idatzita dago. +Baina, demagun etiketak nabigatzailean kargatu nahi ditudala eta haien interpretazioa ekidin nahi dudala. Orduan, HTMLan esanahi berezia duten parentesi angeluarretatik ihes egin behar dut. Hona hemen ihes egindako HTMLa: +<strong>Testu hau letra lodiz idatzita dago.</strong> + + +

    + +### Blog aipua: "OWASPek segurtasunera bideratutako kodeketa liburutegia erabiltzea gomendatzen du" + +OWASP [XSS (Cross Site Scripting) Prebentzio tranpa orria](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) bloga: +> "Kodetzaile horiek idaztea ez da oso zaila, baina tranpa ugari daude ezkutuan. Adibidez, Javascripten bezalako lasterbide batzuk" erabiltzeko tentazioa izan dezakezu. Hala ere, balio horiek arriskutsuak dira, eta nabigatzailean habiaratutako analizatzaileek oker interpreta ditzakete. Baliteke zuri ahaztea ihes egitea ihes pertsonaiarengandik, erasotzaileek erabil dezaketeena neutralizatzeko zure segurtasun ahaleginak. **OWASPek gomendatzen du segurtasunera bideratutako kodeketa liburutegiak erabiltzea, arauak behar bezala ezartzen direla ziurtatzeko**." + +

    + +### Blog aipua: "Ihes sintaxia erabili behar duzu HTML zatian" + +OWASP [XSS (Cross Site Scripting) Prebentzio tranpa orria](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) bloga: +> "Baina HTML entitate kodeketak ez du ondo funtzionatzen
    `. Hori arindu daiteke arakatzaileari aginduz konfiantzazko datuen zatiak edukitzat soilik tratatzeko eta inoiz ez interpretatzeko. Teknika horri ihes egitea deritzo. Npm liburutegi eta HTML txantiloi motor askok ihes egiteko baliabideak eskaintzen dituzte (adibidez: [escape-html](https://github.com/component/escape-html), [node-esapi](https://github.com/ESAPI/node-esapi))). HTML edukiak ez ezik CSSk eta JavaScriptek ere ihes egin beharko lukete + + + +### Kode adibidea: ez jarri fidagarritasunik gabeko daturik zure HTMLan + +```javascript + zuzenean scriptean + + HTML komentario baten barruan + +
    ezaugarri izen batean + + tag izen batean + + CSSan zuzenean + +``` + +### Kode adibidea: datu base batean txerta daitekeen eduki kaltegarria + +```javascript +
    + Komentario bat + +
    + +``` + +

    + +### Blog aipua: "Pertsonaiak interpretatuak izatea nahi ez dugunean" + +[benramsey.com](https://benramsey.com/articles/escape-output/) bloga: +> Datuak modu askotara irten daitezke zure aplikaziotik: web nabigatzaile bati bidalitako HTML moduan, SQL datu basera bidalita, XML RSS irakurgailura bidalita, WML haririk gabeko gailu batera bidalita, etab. Aukerak mugagabeak dira. Horietako bakoitzak bere karaktere bereziak ditu, multzoka jasotzen dituena, eta jasotako gainerako testu arruntaren aldean desberdin interpretatzen dena. Batzuetan, karaktere berezi horiek bidali nahi ditugu interpretatuak izan ahal izateko (HTML nabigatzaile batera bidalitako HTML etiketak, adibidez); beste batzuetan (erabiltzaileek edo beste iturri batzuek egindako sarreren kasuan), ez dugu nahi karaktere horiek interpretatuak izan daitezen, eta, beraz, ihes egin behar diegu. + +> Ihes egiteari kodetzea ere esaten zaio batzuetan: ihes egitea edo kodetzea, laburbilduz,, datuak egikaritu edo interpretatuko ez diren moduan irudikatzeko prozesua da, alegia. Adibidez, HTMLk honako testu hau letra lodiz idatziko du web nabigatzaile batean etiketek esanahi berezia dutelako: +Testu hau letra lodiz idatzita dago. +Baina, demagun etiketak nabigatzailean kargatu nahi ditudala eta haien interpretazioa ekidin nahi dudala. Orduan, HTMLan esanahi berezia duten parentesi angeluarretatik ihes egin behar dut. Hona hemen ihes egindako HTMLa: +<strong>Testu hau letra lodiz idatzita dago.</strong> + + +

    + +### Blog aipua: "OWASPek segurtasunera bideratutako kodeketa liburutegia erabiltzea gomendatzen du" + +OWASP [XSS (Cross Site Scripting) Prebentzio tranpa orria](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) bloga: +> "Kodetzaile horiek idaztea ez da oso zaila, baina tranpa ugari daude ezkutuan. Adibidez, Javascripten bezalako lasterbide batzuk" erabiltzeko tentazioa izan dezakezu. Hala ere, balio horiek arriskutsuak dira, eta nabigatzailean habiaratutako analizatzaileek oker interpreta ditzakete. Baliteke zuri ahaztea ihes egitea ihes pertsonaiarengandik, erasotzaileek erabil dezaketeena neutralizatzeko zure segurtasun ahaleginak. **OWASPek gomendatzen du segurtasunera bideratutako kodeketa liburutegiak erabiltzea, arauak behar bezala ezartzen direla ziurtatzeko**." + +

    + +### Blog aipua: "Ihes sintaxia erabili behar duzu HTML zatian" + +OWASP [XSS (Cross Site Scripting) Prebentzio tranpa orria](https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) bloga: +> "Baina HTML entitate kodeketak ez du ondo funtzionatzen zuzenean scriptean HTML komentario baten barruan @@ -23,7 +23,7 @@ HTML eta beste web lengoaia batzuek kode egikarigarriarekin nahasten dute edukia ### Kode adibidea: datu base batean txerta daitekeen eduki kaltegarria -```javascript +```html
    Komentario bat direto em um script dentro de um comentário HTML @@ -22,7 +22,7 @@ HTML e outras linguagens da Web combinam conteúdo com código executável - um ### Exemplo de código - Conteúdo mal-intencionado que pode ser injetado em um banco de dados -```javascript +```html
    A pseudo comment to the a post directly in a script inside an HTML comment @@ -22,7 +22,7 @@ HTML and other web languages mix content with executable code - a single HTML pa ### Code example - Malicious content that might be injected into a DB -```javascript +```html
    A pseudo comment to the a post directly in a script inside an HTML comment @@ -22,7 +22,7 @@ HTML and other web languages mix content with executable code - a single HTML pa ### Code example - Malicious content that might be injected into a DB -```javascript +```html
    A pseudo comment to the a post directly in a script inside an HTML comment @@ -21,7 +21,7 @@ HTML i inne języki internetowe mieszają zawartość z kodem wykonywalnym - poj ### Przykład kodu - złośliwe treści, które mogą zostać wstrzyknięte do bazy danych -```javascript +```html
    A pseudo comment to the a post directly in a script inside an HTML comment @@ -22,7 +22,7 @@ HTML и другие веб-языки смешивают контент с ис ### Пример кода - вредоносный контент, который может быть введен в БД -```javascript +```html
    A pseudo comment to the a post ",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}}); \ No newline at end of file diff --git a/.operations/res/normalize.css b/.operations/res/normalize.css deleted file mode 100644 index 81c6f31ea..000000000 --- a/.operations/res/normalize.css +++ /dev/null @@ -1,427 +0,0 @@ -/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ - -/** - * 1. Set default font family to sans-serif. - * 2. Prevent iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-family: sans-serif; /* 1 */ - -ms-text-size-adjust: 100%; /* 2 */ - -webkit-text-size-adjust: 100%; /* 2 */ -} - -/** - * Remove default margin. - */ - -body { - margin: 0; -} - -/* HTML5 display definitions - ========================================================================== */ - -/** - * Correct `block` display not defined for any HTML5 element in IE 8/9. - * Correct `block` display not defined for `details` or `summary` in IE 10/11 - * and Firefox. - * Correct `block` display not defined for `main` in IE 11. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} - -/** - * 1. Correct `inline-block` display not defined in IE 8/9. - * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. - */ - -audio, -canvas, -progress, -video { - display: inline-block; /* 1 */ - vertical-align: baseline; /* 2 */ -} - -/** - * Prevent modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Address `[hidden]` styling not present in IE 8/9/10. - * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. - */ - -[hidden], -template { - display: none; -} - -/* Links - ========================================================================== */ - -/** - * Remove the gray background color from active links in IE 10. - */ - -a { - background-color: transparent; -} - -/** - * Improve readability when focused and also mouse hovered in all browsers. - */ - -a:active, -a:hover { - outline: 0; -} - -/* Text-level semantics - ========================================================================== */ - -/** - * Address styling not present in IE 8/9/10/11, Safari, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/** - * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. - */ - -b, -strong { - font-weight: bold; -} - -/** - * Address styling not present in Safari and Chrome. - */ - -dfn { - font-style: italic; -} - -/** - * Address variable `h1` font-size and margin within `section` and `article` - * contexts in Firefox 4+, Safari, and Chrome. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/** - * Address styling not present in IE 8/9. - */ - -mark { - background: #ff0; - color: #000; -} - -/** - * Address inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Remove border when inside `a` element in IE 8/9/10. - */ - -img { - border: 0; -} - -/** - * Correct overflow not hidden in IE 9/10/11. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Grouping content - ========================================================================== */ - -/** - * Address margin not present in IE 8/9 and Safari. - */ - -figure { - margin: 1em 40px; -} - -/** - * Address differences between Firefox and other browsers. - */ - -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} - -/** - * Contain overflow in all browsers. - */ - -pre { - overflow: auto; -} - -/** - * Address odd `em`-unit font size rendering in all browsers. - */ - -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} - -/* Forms - ========================================================================== */ - -/** - * Known limitation: by default, Chrome and Safari on OS X allow very limited - * styling of `select`, unless a `border` property is set. - */ - -/** - * 1. Correct color not being inherited. - * Known issue: affects color of disabled elements. - * 2. Correct font properties not being inherited. - * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. - */ - -button, -input, -optgroup, -select, -textarea { - color: inherit; /* 1 */ - font: inherit; /* 2 */ - margin: 0; /* 3 */ -} - -/** - * Address `overflow` set to `hidden` in IE 8/9/10/11. - */ - -button { - overflow: visible; -} - -/** - * Address inconsistent `text-transform` inheritance for `button` and `select`. - * All other form control elements do not inherit `text-transform` values. - * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. - * Correct `select` style inheritance in Firefox. - */ - -button, -select { - text-transform: none; -} - -/** - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Correct inability to style clickable `input` types in iOS. - * 3. Improve usability and consistency of cursor style between image-type - * `input` and others. - */ - -button, -html input[type="button"], /* 1 */ -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; /* 2 */ - cursor: pointer; /* 3 */ -} - -/** - * Re-set default cursor for disabled elements. - */ - -button[disabled], -html input[disabled] { - cursor: default; -} - -/** - * Remove inner padding and border in Firefox 4+. - */ - -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} - -/** - * Address Firefox 4+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -input { - line-height: normal; -} - -/** - * It's recommended that you don't attempt to style these elements. - * Firefox's implementation doesn't respect box-sizing, padding, or width. - * - * 1. Address box sizing set to `content-box` in IE 8/9/10. - * 2. Remove excess padding in IE 8/9/10. - */ - -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Fix the cursor style for Chrome's increment/decrement buttons. For certain - * `font-size` values of the `input`, it causes the cursor style of the - * decrement button to change from `default` to `text`. - */ - -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Address `appearance` set to `searchfield` in Safari and Chrome. - * 2. Address `box-sizing` set to `border-box` in Safari and Chrome - * (include `-moz` to future-proof). - */ - -input[type="search"] { - -webkit-appearance: textfield; /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; /* 2 */ - box-sizing: content-box; -} - -/** - * Remove inner padding and search cancel button in Safari and Chrome on OS X. - * Safari (but not Chrome) clips the cancel button when the search input has - * padding (and `textfield` appearance). - */ - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct `color` not being inherited in IE 8/9/10/11. - * 2. Remove padding so people aren't caught out if they zero out fieldsets. - */ - -legend { - border: 0; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Remove default vertical scrollbar in IE 8/9/10/11. - */ - -textarea { - overflow: auto; -} - -/** - * Don't inherit the `font-weight` (applied by a rule above). - * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. - */ - -optgroup { - font-weight: bold; -} - -/* Tables - ========================================================================== */ - -/** - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -} - -td, -th { - padding: 0; -} \ No newline at end of file diff --git a/.operations/res/skeleton.css b/.operations/res/skeleton.css deleted file mode 100644 index f28bf6c59..000000000 --- a/.operations/res/skeleton.css +++ /dev/null @@ -1,418 +0,0 @@ -/* -* Skeleton V2.0.4 -* Copyright 2014, Dave Gamache -* www.getskeleton.com -* Free to use under the MIT license. -* http://www.opensource.org/licenses/mit-license.php -* 12/29/2014 -*/ - - -/* Table of contents -–––––––––––––––––––––––––––––––––––––––––––––––––– -- Grid -- Base Styles -- Typography -- Links -- Buttons -- Forms -- Lists -- Code -- Tables -- Spacing -- Utilities -- Clearing -- Media Queries -*/ - - -/* Grid -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.container { - position: relative; - width: 100%; - max-width: 960px; - margin: 0 auto; - padding: 0 20px; - box-sizing: border-box; } -.column, -.columns { - width: 100%; - float: left; - box-sizing: border-box; } - -/* For devices larger than 400px */ -@media (min-width: 400px) { - .container { - width: 85%; - padding: 0; } -} - -/* For devices larger than 550px */ -@media (min-width: 550px) { - .container { - width: 80%; } - .column, - .columns { - margin-left: 4%; } - .column:first-child, - .columns:first-child { - margin-left: 0; } - - .one.column, - .one.columns { width: 4.66666666667%; } - .two.columns { width: 13.3333333333%; } - .three.columns { width: 22%; } - .four.columns { width: 30.6666666667%; } - .five.columns { width: 39.3333333333%; } - .six.columns { width: 48%; } - .seven.columns { width: 56.6666666667%; } - .eight.columns { width: 65.3333333333%; } - .nine.columns { width: 74.0%; } - .ten.columns { width: 82.6666666667%; } - .eleven.columns { width: 91.3333333333%; } - .twelve.columns { width: 100%; margin-left: 0; } - - .one-third.column { width: 30.6666666667%; } - .two-thirds.column { width: 65.3333333333%; } - - .one-half.column { width: 48%; } - - /* Offsets */ - .offset-by-one.column, - .offset-by-one.columns { margin-left: 8.66666666667%; } - .offset-by-two.column, - .offset-by-two.columns { margin-left: 17.3333333333%; } - .offset-by-three.column, - .offset-by-three.columns { margin-left: 26%; } - .offset-by-four.column, - .offset-by-four.columns { margin-left: 34.6666666667%; } - .offset-by-five.column, - .offset-by-five.columns { margin-left: 43.3333333333%; } - .offset-by-six.column, - .offset-by-six.columns { margin-left: 52%; } - .offset-by-seven.column, - .offset-by-seven.columns { margin-left: 60.6666666667%; } - .offset-by-eight.column, - .offset-by-eight.columns { margin-left: 69.3333333333%; } - .offset-by-nine.column, - .offset-by-nine.columns { margin-left: 78.0%; } - .offset-by-ten.column, - .offset-by-ten.columns { margin-left: 86.6666666667%; } - .offset-by-eleven.column, - .offset-by-eleven.columns { margin-left: 95.3333333333%; } - - .offset-by-one-third.column, - .offset-by-one-third.columns { margin-left: 34.6666666667%; } - .offset-by-two-thirds.column, - .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } - - .offset-by-one-half.column, - .offset-by-one-half.columns { margin-left: 52%; } - -} - - -/* Base Styles -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -/* NOTE -html is set to 62.5% so that all the REM measurements throughout Skeleton -are based on 10px sizing. So basically 1.5rem = 15px :) */ -html { - font-size: 62.5%; } -body { - font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ - line-height: 1.6; - font-weight: 400; - font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #222; } - - -/* Typography -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -h1, h2, h3, h4, h5, h6 { - margin-top: 0; - margin-bottom: 2rem; - font-weight: 300; } -h1 { font-size: 4.0rem; line-height: 1.2; letter-spacing: -.1rem;} -h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; } -h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; } -h4 { font-size: 2.4rem; line-height: 1.35; letter-spacing: -.08rem; } -h5 { font-size: 1.8rem; line-height: 1.5; letter-spacing: -.05rem; } -h6 { font-size: 1.5rem; line-height: 1.6; letter-spacing: 0; } - -/* Larger than phablet */ -@media (min-width: 550px) { - h1 { font-size: 5.0rem; } - h2 { font-size: 4.2rem; } - h3 { font-size: 3.6rem; } - h4 { font-size: 3.0rem; } - h5 { font-size: 2.4rem; } - h6 { font-size: 1.5rem; } -} - -p { - margin-top: 0; } - - -/* Links -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -a { - color: #1EAEDB; } -a:hover { - color: #0FA0CE; } - - -/* Buttons -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.button, -button, -input[type="submit"], -input[type="reset"], -input[type="button"] { - display: inline-block; - height: 38px; - padding: 0 30px; - color: #555; - text-align: center; - font-size: 11px; - font-weight: 600; - line-height: 38px; - letter-spacing: .1rem; - text-transform: uppercase; - text-decoration: none; - white-space: nowrap; - background-color: transparent; - border-radius: 4px; - border: 1px solid #bbb; - cursor: pointer; - box-sizing: border-box; } -.button:hover, -button:hover, -input[type="submit"]:hover, -input[type="reset"]:hover, -input[type="button"]:hover, -.button:focus, -button:focus, -input[type="submit"]:focus, -input[type="reset"]:focus, -input[type="button"]:focus { - color: #333; - border-color: #888; - outline: 0; } -.button.button-primary, -button.button-primary, -input[type="submit"].button-primary, -input[type="reset"].button-primary, -input[type="button"].button-primary { - color: #FFF; - background-color: #33C3F0; - border-color: #33C3F0; } -.button.button-primary:hover, -button.button-primary:hover, -input[type="submit"].button-primary:hover, -input[type="reset"].button-primary:hover, -input[type="button"].button-primary:hover, -.button.button-primary:focus, -button.button-primary:focus, -input[type="submit"].button-primary:focus, -input[type="reset"].button-primary:focus, -input[type="button"].button-primary:focus { - color: #FFF; - background-color: #1EAEDB; - border-color: #1EAEDB; } - - -/* Forms -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -input[type="email"], -input[type="number"], -input[type="search"], -input[type="text"], -input[type="tel"], -input[type="url"], -input[type="password"], -textarea, -select { - height: 38px; - padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ - background-color: #fff; - border: 1px solid #D1D1D1; - border-radius: 4px; - box-shadow: none; - box-sizing: border-box; } -/* Removes awkward default styles on some inputs for iOS */ -input[type="email"], -input[type="number"], -input[type="search"], -input[type="text"], -input[type="tel"], -input[type="url"], -input[type="password"], -textarea { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; } -textarea { - min-height: 65px; - padding-top: 6px; - padding-bottom: 6px; } -input[type="email"]:focus, -input[type="number"]:focus, -input[type="search"]:focus, -input[type="text"]:focus, -input[type="tel"]:focus, -input[type="url"]:focus, -input[type="password"]:focus, -textarea:focus, -select:focus { - border: 1px solid #33C3F0; - outline: 0; } -label, -legend { - display: block; - margin-bottom: .5rem; - font-weight: 600; } -fieldset { - padding: 0; - border-width: 0; } -input[type="checkbox"], -input[type="radio"] { - display: inline; } -label > .label-body { - display: inline-block; - margin-left: .5rem; - font-weight: normal; } - - -/* Lists -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -ul { - list-style: circle inside; } -ol { - list-style: decimal inside; } -ol, ul { - padding-left: 0; - margin-top: 0; } -ul ul, -ul ol, -ol ol, -ol ul { - margin: 1.5rem 0 1.5rem 3rem; - font-size: 90%; } -li { - margin-bottom: 1rem; } - - -/* Code -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -code { - padding: .2rem .5rem; - margin: 0 .2rem; - font-size: 90%; - white-space: nowrap; - background: #F1F1F1; - border: 1px solid #E1E1E1; - border-radius: 4px; } -pre > code { - display: block; - padding: 1rem 1.5rem; - white-space: pre; } - - -/* Tables -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -th, -td { - padding: 12px 15px; - text-align: left; - border-bottom: 1px solid #E1E1E1; } -th:first-child, -td:first-child { - padding-left: 0; } -th:last-child, -td:last-child { - padding-right: 0; } - - -/* Spacing -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -button, -.button { - margin-bottom: 1rem; } -input, -textarea, -select, -fieldset { - margin-bottom: 1.5rem; } -pre, -blockquote, -dl, -figure, -table, -p, -ul, -ol, -form { - margin-bottom: 2.5rem; } - - -/* Utilities -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -.u-full-width { - width: 100%; - box-sizing: border-box; } -.u-max-full-width { - max-width: 100%; - box-sizing: border-box; } -.u-pull-right { - float: right; } -.u-pull-left { - float: left; } - - -/* Misc -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -hr { - margin-top: 3rem; - margin-bottom: 3.5rem; - border-width: 0; - border-top: 1px solid #E1E1E1; } - - -/* Clearing -–––––––––––––––––––––––––––––––––––––––––––––––––– */ - -/* Self Clearing Goodness */ -.container:after, -.row:after, -.u-cf { - content: ""; - display: table; - clear: both; } - - -/* Media Queries -–––––––––––––––––––––––––––––––––––––––––––––––––– */ -/* -Note: The best way to structure the use of media queries is to create the queries -near the relevant code. For example, if you wanted to change the styles for buttons -on small devices, paste the mobile query code up in the buttons section and style it -there. -*/ - - -/* Larger than mobile */ -@media (min-width: 400px) {} - -/* Larger than phablet (also point when grid becomes active) */ -@media (min-width: 550px) {} - -/* Larger than tablet */ -@media (min-width: 750px) {} - -/* Larger than desktop */ -@media (min-width: 1000px) {} - -/* Larger than Desktop HD */ -@media (min-width: 1200px) {} diff --git a/.operations/res/template.html b/.operations/res/template.html deleted file mode 100644 index 735d4f393..000000000 --- a/.operations/res/template.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - Node Best Practices - - - - - - -
    -
    -
    -
    - - - - - - - - - \ No newline at end of file From 9d7a86386885cd3a4aa393934e1f62380aafd958 Mon Sep 17 00:00:00 2001 From: Sagi Levi <95089762+sagi403@users.noreply.github.com> Date: Sun, 16 Apr 2023 12:37:19 +0300 Subject: [PATCH 1679/1795] Update the Privatenpm.png file Update the image of the private npm diagram to a better-quality one, to be more in line with other images. --- assets/images/Privatenpm.png | Bin 93391 -> 81492 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/images/Privatenpm.png b/assets/images/Privatenpm.png index 9038636d8c6d0b1f3e6243971db7c797b111dfd6..d14466f7830f3ee28f6abfd88c91798614c16d19 100644 GIT binary patch literal 81492 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

    KT7-^&6F#)p3es2d+iR3kUq$HTfv{p$(C00( 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 literal 93391 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 MlvR6OEo~9 Date: Wed, 19 Apr 2023 21:12:45 +0000 Subject: [PATCH 1680/1795] update the last update badge to today [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fcaf4dec..d598ab68e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@


    From 32ea01f410114323cef94f1e138adfe8877a5377 Mon Sep 17 00:00:00 2001 From: Spencer C <109806759+SpencerIsGiddy@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:22:34 -0400 Subject: [PATCH 1681/1795] fix conflict --- .../lint-and-generate-html-from-markdown.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/lint-and-generate-html-from-markdown.yml b/.github/workflows/lint-and-generate-html-from-markdown.yml index 77bcd3a37..721c4da99 100644 --- a/.github/workflows/lint-and-generate-html-from-markdown.yml +++ b/.github/workflows/lint-and-generate-html-from-markdown.yml @@ -28,22 +28,3 @@ jobs: - run: npm install - run: npm run lint - - build: - name: Build - runs-on: ubuntu-20.04 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Setup Node.js environment - uses: actions/setup-node@v3 - with: - node-version: '18' - - - run: npm install - - run: npm run build - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - IS_FORK: ${{ github.repository != 'goldbergyoni/nodebestpractices' }} From 194a827cec4006d11df028a6a88397d57a851d59 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 4 May 2023 12:00:41 +0300 Subject: [PATCH 1682/1795] More edits --- README.md | 103 ++++++++++++------ .../projectstructre/breakintcomponents.md | 34 +++++- sections/projectstructre/createlayers.md | 25 ++++- sections/projectstructre/separateexpress.md | 100 ----------------- 4 files changed, 123 insertions(+), 139 deletions(-) delete mode 100644 sections/projectstructre/separateexpress.md diff --git a/README.md b/README.md index d598ab68e..0f0ebb908 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    - 1. Project Structure Practices (5) + 1. Project Architecture Practices (5)   [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-components)
    @@ -114,7 +114,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [4.4 Detect code issues with a linter](#-44-detect-code-issues-with-a-linter)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 Constantly inspect for vulnerable dependencies](#-46-constantly-inspect-for-vulnerable-dependencies)
    -  [4.7 Tag your tests `#advanced`](#-47-tag-your-tests)
    +  [4.7 Tag your tests `#advanced`](#-47-tag-your-tests)
      [4.8 Check your test coverage, it helps to identify wrong test patterns](#-48-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.9 Inspect for outdated packages](#-49-inspect-for-outdated-packages)
      [4.10 Use production-like environment for e2e testing](#-410-use-production-like-environment-for-e2e-testing)
    @@ -136,7 +136,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
      [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    -  [5.8. Discover errors and downtime using APM products `#advanced`](#-58-discover-errors-and-downtime-using-apm-products)
    +  [5.8. Discover errors and downtime using APM products `#advanced`](#-58-discover-errors-and-downtime-using-apm-products)
      [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
      [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
      [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
    @@ -144,7 +144,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [5.13. Use tools that automatically detect vulnerabilities](#-513-use-tools-that-automatically-detect-vulnerabilities)
      [5.14. Assign a transaction id to each log statement `#advanced`](#-514-assign-a-transaction-id-to-each-log-statement)
      [5.15. Set NODE_ENV=production](#-515-set-node_envproduction)
    -  [5.16. Design automated, atomic and zero-downtime deployments `#advanced`](#-516-design-automated-atomic-and-zero-downtime-deployments)
    +  [5.16. Design automated, atomic and zero-downtime deployments `#advanced`](#-516-design-automated-atomic-and-zero-downtime-deployments)
      [5.17. Use an LTS release of Node.js](#-517-use-an-lts-release-of-nodejs)
      [5.18. Don't route logs within the app](#-518-dont-route-logs-within-the-app)
      [5.19. Install your packages with npm ci `#new`](#-519-install-your-packages-with-npm-ci)
    @@ -158,7 +158,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [6.1. Embrace linter security rules](#-61-embrace-linter-security-rules)
      [6.2. Limit concurrent requests using a middleware](#-62-limit-concurrent-requests-using-a-middleware)
    -  [6.3 Extract secrets from config files or use packages to encrypt them `#strategic`](#-63-extract-secrets-from-config-files-or-use-packages-to-encrypt-them)
    +  [6.3 Extract secrets from config files or use packages to encrypt them `#strategic`](#-63-extract-secrets-from-config-files-or-use-packages-to-encrypt-them)
      [6.4. Prevent query injection vulnerabilities with ORM/ODM libraries `#strategic`](#-64-prevent-query-injection-vulnerabilities-with-ormodm-libraries)
      [6.5. Collection of generic security best practices](#-65-collection-of-generic-security-best-practices)
      [6.6. Adjust the HTTP response headers for enhanced security](#-66-adjust-the-http-response-headers-for-enhanced-security)
    @@ -174,13 +174,14 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [6.16. Prevent evil RegEx from overloading your single thread execution](#-616-prevent-evil-regex-from-overloading-your-single-thread-execution)
      [6.17. Avoid module loading using a variable](#-617-avoid-module-loading-using-a-variable)
      [6.18. Run unsafe code in a sandbox](#-618-run-unsafe-code-in-a-sandbox)
    -  [6.19. Take extra care when working with child processes `#advanced`](#-619-take-extra-care-when-working-with-child-processes)
    +  [6.19. Take extra care when working with child processes `#advanced`](#-619-take-extra-care-when-working-with-child-processes)
      [6.20. Hide error details from clients](#-620-hide-error-details-from-clients)
      [6.21. Configure 2FA for npm or Yarn `#strategic`](#-621-configure-2fa-for-npm-or-yarn)
      [6.22. Modify session middleware settings](#-622-modify-session-middleware-settings)
    -  [6.23. Avoid DOS attacks by explicitly setting when a process should crash `#advanced`](#-623-avoid-dos-attacks-by-explicitly-setting-when-a-process-should-crash)
    +  [6.23. Avoid DOS attacks by explicitly setting when a process should crash `#advanced`](#-623-avoid-dos-attacks-by-explicitly-setting-when-a-process-should-crash)
      [6.24. Prevent unsafe redirects](#-624-prevent-unsafe-redirects)
      [6.25. Avoid publishing secrets to the npm registry](#-625-avoid-publishing-secrets-to-the-npm-registry)
    +  [6.26. Import built-in modules using the 'node:' protocol #new](#-626-import-built-in-modules-using-the-'node:'-protocol)
    @@ -204,38 +205,60 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [8.3. Let the Docker runtime handle replication and uptime `#strategic`](#-83-let-the-docker-runtime-handle-replication-and-uptime)
      [8.4. Use .dockerignore to prevent leaking secrets](#-84-use-dockerignore-to-prevent-leaking-secrets)
      [8.5. Clean-up dependencies before production](#-85-clean-up-dependencies-before-production)
    -  [8.6. Shutdown smartly and gracefully `#advanced`](#-86-shutdown-smartly-and-gracefully)
    +  [8.6. Shutdown smartly and gracefully `#advanced`](#-86-shutdown-smartly-and-gracefully)
      [8.7. Set memory limits using both Docker and v8 `#advanced #strategic`](#-87-set-memory-limits-using-both-docker-and-v8)
      [8.8. Plan for efficient caching](#-88-plan-for-efficient-caching)
      [8.9. Use explicit image reference, avoid latest tag](#-89-use-explicit-image-reference-avoid-latest-tag)
      [8.10. Prefer smaller Docker base images](#-810-prefer-smaller-docker-base-images)
    -  [8.11. Clean-out build-time secrets, avoid secrets in args `#strategic #new`](#-811-clean-out-build-time-secrets-avoid-secrets-in-args)
    -  [8.12. Scan images for multi layers of vulnerabilities `#advanced`](#-812-scan-images-for-multi-layers-of-vulnerabilities)
    +  [8.11. Clean-out build-time secrets, avoid secrets in args `#strategic #new`](#-811-clean-out-build-time-secrets-avoid-secrets-in-args)
    +  [8.12. Scan images for multi layers of vulnerabilities `#advanced`](#-812-scan-images-for-multi-layers-of-vulnerabilities)
      [8.13 Clean NODE_MODULE cache](#-813-clean-node_module-cache)
      [8.14. Generic Docker practices](#-814-generic-docker-practices)
    -  [8.15. Lint your Dockerfile `#new`](#-815-lint-your-dockerfile)
    +  [8.15. Lint your Dockerfile `#new`](#-815-lint-your-dockerfile)


    -# `1. Project Structure Practices` +# `1. Project Architecture Practices` -## ![✔] 1.1 Structure your solution by components +## ![✔] 1.1 Structure your solution by business components -**TL;DR:** The worst large applications pitfall is maintaining a huge code base with hundreds of dependencies - such a monolith slows down developers as they try to incorporate new features. Instead, partition your code into components, each gets its folder or a dedicated codebase, and ensure that each unit is kept small and simple. Visit 'Read More' below to see examples of correct project structure +**TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like user-component, order-component, etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a more granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo -**Otherwise:** When developers who code new features struggle to realize the impact of their change and fear to break other dependent components - deployments become slower and riskier. It's also considered harder to scale-out when all the business units are not separated +```bash +my-system +├─ apps (components) +│ ├─ orders +│ ├─ users +│ ├─ payments +├─ libraries (generic cross-component functionality) +│ ├─ logger +│ ├─ authenticator +``` + +**Otherwise:** when artifacts from various modules/topics are mixed together, there are great chances of a tightly-coupled 'spaghetti' system. For example, when 'module-a' controller might call 'module-b service', every code change might affect any other module and file. With this approach, developers who code new features struggle to realize the scope and impact of their change. Consequently, they fear breaking other modules, and each deployment becomes slower and riskier 🔗 [**Read More: structure by components**](./sections/projectstructre/breakintcomponents.md)

    -## ![✔] 1.2 Layer your components, keep the web layer within its boundaries +## ![✔] 1.2 Layer your components with 3-tiers, keep the web layer within its boundaries -**TL;DR:** Each component should contain 'layers' - a dedicated object for the web, logic, and data access code. This not only draws a clean separation of concerns but also significantly eases mocking and testing the system. Though this is a very common pattern, API developers tend to mix layers by passing the web layer objects (e.g. Express req, res) to business logic and data layers - this makes your application dependent on and accessible only by specific web frameworks +**TL;DR:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal + +```bash +my-system +├─ apps (components) +│ ├─ component-a + │ ├─ entry-points + │ │ ├─ api # controller comes here + │ │ ├─ message-queue # message consumer comes here + │ ├─ domain # features and flows: DTO, services, logic + │ ├─ data-access # DB calls w/o ORM +``` -**Otherwise:** App that mixes web objects with other layers cannot be accessed by testing code, CRON jobs, triggers from message queues, etc +**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access the the logic by other clients like testing code, scheduled jobs, message queues, etc 🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) @@ -251,23 +274,21 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin

    -## ![✔] 1.4 Separate Express 'app' and 'server' +## ![✔] 1.4 Use environment aware, secure and hierarchical config -**TL;DR:** Avoid the nasty habit of defining the entire [Express](https://expressjs.com/) app in a single huge file - separate your 'Express' definition to at least two files: the API declaration (app.js) and the networking concerns (WWW). For even better structure, locate your API declaration within components - -**Otherwise:** Your API will be accessible for testing via HTTP calls only (slower and much harder to generate coverage reports). It probably won't be a big pleasure to maintain hundreds of lines of code in a single file +**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict). -🔗 [**Read More: separate Express 'app' and 'server'**](./sections/projectstructre/separateexpress.md) +**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or DevOps team. Probably both -

    +🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) -## ![✔] 1.5 Use environment aware, secure and hierarchical config +## ![✔] 1.5 Use TypeScript sparingly and thoughtfully -**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict). +**TL;DR:** Coding without type safety is no longer an option - TypeScript is the most popular option for JavaScript with types. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly for simple types only -**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or DevOps team. Probably both +**Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time -🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) +🔗 [**Read More: TypeScript considerations**](./sections/projectstructre/typescript-considerations.md)


    @@ -434,8 +455,7 @@ function someFunction() { } // Avoid -function someFunction() -{ +function someFunction() { // code block } ``` @@ -508,7 +528,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function ```javascript // for global variables names we use the const/let keyword and UPPER_SNAKE_CASE -let MUTABLE_GLOBAL = "mutable value" +let MUTABLE_GLOBAL = "mutable value"; const GLOBAL_CONSTANT = "immutable value"; const CONFIG = { key: "value", @@ -1236,6 +1256,27 @@ Also known as correlation id / transit id / tracing id / request id / request co **Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. 🔗 [**Read More: Avoid publishing secrets**](./sections/security/avoid_publishing_secrets.md) + +## ![✔] 6.26. Import built-in modules using the 'node:' protocol + + + +**TL;DR:** Import or require built-in Node.js modules using the 'node protocol' syntax: + +```javascript +import { functionName } from "node:module"; // note that 'node:' prefix +``` + +For example: + +```javascript +import { createServer } from "node:http"; +``` + +This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to the official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) + +**Otherwise:** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them +


    ⬆ Return to top

    diff --git a/sections/projectstructre/breakintcomponents.md b/sections/projectstructre/breakintcomponents.md index 297daaa55..91df46d17 100644 --- a/sections/projectstructre/breakintcomponents.md +++ b/sections/projectstructre/breakintcomponents.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -For medium sized apps and above, monoliths are really bad - having one big software with many dependencies is just hard to reason about and often leads to spaghetti code. Even smart architects — those who are skilled enough to tame the beast and 'modularize' it — spend great mental effort on design, and each change requires carefully evaluating the impact on other dependent objects. The ultimate solution is to develop small software: divide the whole stack into self-contained components that don't share files with others, each constitutes very few files (e.g. API, service, data access, test, etc.) so that it's very easy to reason about it. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. Both are good as long as you keep the software complexity low. The very least you should do is create basic borders between components, assign a folder in your project root for each business component and make it self-contained - other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows. +For medium sized apps and above, *non-modular* monoliths are really bad - having one big software with 'spaghetti' of dependencies is just hard to reason about. The ultimate solution is to develop smaller software: divide the whole stack into self-contained components that don't share files with others, each is a standalone logical app (e.g. has its own API, service, data access, test, etc.) so that onboarding into it and changing the code is much easier than dealing with the whole system. Some may call this 'microservices' architecture — it's important to understand that microservices are not a spec which you must follow, but rather a set of principles. You may adopt many principles into a full-blown microservices architecture or adopt only a few. The very least you should do is create basic borders between components, assign a folder or repository in your system root for each business component and make it self-contained. Other components are allowed to consume its functionality only through its public interface or API. This is the foundation for keeping your components simple, avoid dependency hell and pave the way to full-blown microservices in the future once your app grows

    @@ -28,10 +28,38 @@ So what does the architecture of your application scream? When you look at the t ### Good: Structure your solution by self-contained components -![alt text](../../assets/images/structurebycomponents.PNG "Structuring solution by components") +```bash +my-system +├─ apps (components) +│ ├─ orders +│ │ ├─ package.json +│ │ ├─ api +│ │ ├─ domain +│ │ ├─ data-access +│ ├─ users +│ ├─ payments +├─ libraries (generic cross-component functionality) +│ ├─ logger +│ ├─ authenticator +``` +

    ### Bad: Group your files by technical role -![alt text](../../assets/images/structurebyroles.PNG "Structuring solution by technical roles") +```bash +my-system +├─ controllers +│ ├─ user-controller.js +│ ├─ order-controller.js +│ ├─ payment-controller.js +├─ services +│ ├─ user-service.js +│ ├─ order-service.js +│ ├─ payment-service.js +├─ models +│ ├─ user-model.js +│ ├─ order-model.js +│ ├─ payment-model.js +``` \ No newline at end of file diff --git a/sections/projectstructre/createlayers.md b/sections/projectstructre/createlayers.md index bd02a1af0..f67f88cec 100644 --- a/sections/projectstructre/createlayers.md +++ b/sections/projectstructre/createlayers.md @@ -2,12 +2,27 @@

    - ### Separate component code into layers: web, services, and Data Access Layer (DAL) +### Separate component code into 3 layers -![alt text](../../assets/images/structurebycomponents.PNG "Separate component code into layers") +The root of every component should hold 3 folders that represent common concerns and stages of every transaction: -

    +```bash +my-system +├─ apps (components) +│ ├─ component-a + │ ├─ entry-points + │ │ ├─ api # controller comes here + │ │ ├─ message-queue # message consumer comes here + │ ├─ domain # features and flows: DTO, services, logic + │ ├─ data-access # DB calls w/o ORM +``` -### 1 min explainer: The downside of mixing layers +**- Entry-points -** This is where requests and flows start, whether it's REST API, Graph, message queue, scheduled jobs or any other _door_ to the application. This layer's responsibility is quite minimal - adapt the payload (e.g., JSON) to the app format, including first validation, call the logic/domain layer and return a response. This is typically achieved with a few lines of code. Many use the term "controller" for this type of code also technically, its just an adapter -![alt text](../../assets/images/keepexpressinweb.gif "The downside of mixing layers") +**- Domain -** This is where the app flows, logic and data live. This layer accepts protocol-agnostic payload, plain JavaScript object and returns one as well. Technically it contains common code objects like services, dto/entities, and clients that call external services. It also typically calls the data-access layer to retrieve or persist information + +**- Data-access -** This is where the app holds code that interacts with DB. Ideally, it should externalize an interface that returns/gets plain JavaScript object that is DB agnostic (also known as the repository-pattern). This layer involves DB helper utilities like query builders, ORMs, DB drivers and other implementation libraries + +**What is the merit? -** When having flexible infrastructure that allows adding more API calls and DB queries promptly, a developer can code a feature faster by focusing on the domain folder. In other words, less time is spent on technical activities and more on activities with added value. This is a ubiquitous trait that is at the heart of most software architectures like DDD, hexagonal, clean-architecture and others. On top of this, when the domain layer is not aware of any edge protocol, it can serve any client and not only HTTP calls + +**Why not MVC or clean architecture? -** The 3-tier pattern strikes a great balance between achieving the separation goal while still keeping the structure simple. It also lacks abstractions - each tier represents real-world physical tier where every request will visit. On the other hand, MVC is a simplistic pattern where the letters VC represent a few lines of a code only and the letter M means anything else. Clean architecture is architecture with high level of abstractions that can achieve even greater separation but the price tag is unproportionally higher due to the increased complexity \ No newline at end of file diff --git a/sections/projectstructre/separateexpress.md b/sections/projectstructre/separateexpress.md deleted file mode 100644 index e35c67d24..000000000 --- a/sections/projectstructre/separateexpress.md +++ /dev/null @@ -1,100 +0,0 @@ -# Separate Express 'app' and 'server' - -

    - -### One Paragraph Explainer - -The latest Express generator comes with a great practice that is worth to keep - the API declaration is separated from the network related configuration (port, protocol, etc). This allows testing the API in-process, without performing network calls, with all the benefits that it brings to the table: fast testing execution and getting coverage metrics of the code. It also allows deploying the same API under flexible and different network conditions. Bonus: better separation of concerns and cleaner code - -

    - -### Code example: API declaration, should reside in app.js/app.ts - -```javascript -const app = express(); -app.use(bodyParser.json()); -app.use('/api/events', events.API); -app.use('/api/forms', forms); -``` - -### Code example: Server network declaration, should reside in /bin/www - -
    -Javascript - -```javascript -const app = require('../app'); -const http = require('http'); - -// Get port from environment and store in Express. -const port = normalizePort(process.env.PORT || '3000'); -app.set('port', port); - -// Create HTTP server. -const server = http.createServer(app); -``` -
    - -
    -Typescript - -```typescript -import app from '../app'; -import http from 'http'; - -// Get port from environment and store in Express. -const port = normalizePort(process.env.PORT || '3000'); -app.set('port', port); - -// Create HTTP server. -const server = http.createServer(app); -``` -
    - -### Example: test your API in-process using supertest (popular testing package) - -
    -Javascript - -```javascript -const request = require('supertest'); -const app = express(); - -app.get('/user', (req, res) => { - res.status(200).json({ name: 'tobi' }); -}); - -request(app) - .get('/user') - .expect('Content-Type', /json/) - .expect('Content-Length', '15') - .expect(200) - .end((err, res) => { - if (err) throw err; - }); -``` -
    - - -
    -Typescript - -```typescript -import * as request from "supertest"; -const app = express(); - -app.get('/user', (req: Request, res: Response) => { - res.status(200).json({ name: 'tobi' }); -}); - -request(app) - .get('/user') - .expect('Content-Type', /json/) - .expect('Content-Length', '15') - .expect(200) - .end((err: Error) => { - if (err) throw err; - }); - -``` -
    From 06819bbfb2767bf9bdcfcccc8721ce6a1ed64e10 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 4 May 2023 15:17:29 +0300 Subject: [PATCH 1683/1795] More edits --- README.md | 2 +- .../typescript-considerations.md | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 sections/projectstructre/typescript-considerations.md diff --git a/README.md b/README.md index 0f0ebb908..653c69e79 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ my-system ## ![✔] 1.5 Use TypeScript sparingly and thoughtfully -**TL;DR:** Coding without type safety is no longer an option - TypeScript is the most popular option for JavaScript with types. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly for simple types only +**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly for simple types only **Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time diff --git a/sections/projectstructre/typescript-considerations.md b/sections/projectstructre/typescript-considerations.md new file mode 100644 index 000000000..c0831a1be --- /dev/null +++ b/sections/projectstructre/typescript-considerations.md @@ -0,0 +1,23 @@ +# Use TypeScript sparingly and thoughtfully + +

    + +### One Paragraph Explainer + +TypeScript has won the community's hearts and is almost a standard for modern JavaScript apps. Compared with plain JS, It brings a much better coding ergonomics, facilitates intellisense even for historical libraries that were written with JavaScript and was proven to [prevent specific type of bugs](https://earlbarr.com/publications/typestudy.pdf). With that, if you look carefully under the hype, TypeScript actually brings two **mutual-exclusive** offerings to the table: type-safety and advanced design constructs like abstract classes, interfaces, namespaces and more. Many teams chose TypeScript for better type safety yet _unintentionally_, without any proper planning, use its for different purposes like OOP. These teams change their design style unintentionally due to the ['law of the instruments'](https://en.wikipedia.org/wiki/Law_of_the_instrument) — a cognitive bias that involves using the tooling in hand whether they are the right choice for the mission or not. In other words, if an 'abstract class' exists in the toolbox — developers will use it. If teams consciously opted for the coding techniques mentioned above — that's fair and legit. For others, positively consider coding with classic JavaScript, plain functions and objects, which are simply decorated with primitive types. The later option is likely to result in lower complexity + +

    + +### Research Quote: "15% less bugs" + +From the research [To Type or Not to Type](https://earlbarr.com/publications/typestudy.pdf) + +> "our central finding is that both static type systems find an important percentage of public bugs: both Flow 0.30 and TypeScript 2.0 successfully detect 15%!" + +

    + +### Blog Quote: "TypeScript will always miss 80% of bugs" + +From the post [The TypeScript tax](https://medium.com/javascript-scene/the-typescript-tax-132ff4cb175b) + +> "Some will argue that TypeScript provides realtime bug feedback, so you can catch the bugs earlier, but so do type inference, lint, and testing... You may argue that these other measures have a cost, but because TypeScript will always miss 80% of bugs, you can’t safely skip them either way, so their cost applies to both sides of the ROI math, and is already factored in" From f4fb78efbce940695e2ed3128fdbed4a1660d32f Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Sun, 7 May 2023 17:53:50 +0300 Subject: [PATCH 1684/1795] New bullet: avoid floating code --- README.md | 28 +++++++++++-- sections/projectstructre/choose-framework.md | 44 ++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 sections/projectstructre/choose-framework.md diff --git a/README.md b/README.md index 653c69e79..c44c92eca 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    - 3. Code Style Practices (12) + 3. Code Style Practices (12)   [3.1 Use ESLint `#strategic`](#-31-use-eslint)
    @@ -282,7 +282,15 @@ my-system 🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) -## ![✔] 1.5 Use TypeScript sparingly and thoughtfully +## ![✔] 1.5 Consider all the consequences when choosing the main framework + +**TL;DR:** When building apps and APIs using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: Nest.js, Fastify, express, and Koa. Click read more for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller deployment units. Fastify is our recommendation for apps with reasonably-sized components that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) + +**Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns + +🔗 [**Read More: configuration best practices**](./sections/projectstructre/choose-framework.md) + +## ![✔] 1.6 Use TypeScript sparingly and thoughtfully **TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly for simple types only @@ -422,7 +430,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi

    ⬆ Return to top

    -# `3. Code Style Practices` +# `3. Code Patterns And Style Practices` ## ![✔] 3.1 Use ESLint @@ -432,6 +440,12 @@ especially if the cause of the abnormal behavior is inside of the missing functi 🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) +## ![✔] 3.2 Avoid effects outside of functions + +**TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code with effects inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns + +**Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored and might fail due to a lack of configuration data +

    ## ![✔] 3.2 Node.js specific plugins @@ -684,6 +698,14 @@ All statements above will return false if used with `===`

    +## ![✔] 4.5 Ensure Node.js version is unified + +**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI Node.js definition) + +**Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed + +

    + ## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test **TL;DR:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records diff --git a/sections/projectstructre/choose-framework.md b/sections/projectstructre/choose-framework.md new file mode 100644 index 000000000..21b32ef0a --- /dev/null +++ b/sections/projectstructre/choose-framework.md @@ -0,0 +1,44 @@ +# Structure your solution by components + +

    + +### Recommended frameworks: Pros and cons + +Unlike other choices, choosing the core framework determines strategic factors like the development style and how likely the team is to hit a wall. We believe that framework popularity is a supreme consideration and put our focus on the top 4 most popular frameworks in terms of downloads and GitHub stars. The text below outlines the pros and cons of each framework and how to match a framework to the right application type + +**express.js** + +Pros: Unmatched popularity; gigantic eco-system of extensions and middleware; simple to learn and use; familiar to almost every Node.js developer; tons of community articles and videos are based on express + +Cons: Covers a small subset of a typical application needs - merely a web server that invokes the app function per URL. Choosing express means leaving a handful of app concerns uncovered; outdated mechanics - no native support for async-await; barely maintained and updated; Slower than others + +**Nest.js** + +Pros: More batteries than any other option - covers many application concern including message queues, scheduled jobs and more; OOP-style is an advantage for teams who appreciate this design style; awesome docs; well-maintained; high popularity with a vibrant community + +Cons: High-level abstractions that cloud built-in Node.js conventions; The inclusion of many features, heavy usage of TypeScript and reference to sophisticated patterns might push teams for increased complexity; Steeper learning curve due to a handful of unique narratives (e.g., interceptors, guards, modules, and more); Highly opinionated + +**Fastify** + +Pros: Relatively simple and lean; mostly based on Node.js/JavaScript standards; relatively shallow learning curve; with its official plugins cover many application concerns though not as rich as Nest.js; + +Cons: Younger than others and not as popular yet; smaller eco-system compared to express and Nest.js + +**Koa** + +Pros When compared with express: it's Simpler and nimbler; modern API with async/await support; better performance + +Cons: Covers a small subset of a typical application needs - leaves a handful of app concerns uncovered; Not as popular as express and Nest.js + +### A brief choosing guide + +**Prefer express.js when** - having an experienced architect onboard _and_ in a need to control the fine-grained pieces of the puzzle. In this circumstances, Koa is also a solid option with a more modern API than express but a much smaller eco-system + +**Prefer Fastify when -** The app consists of reasonably-sized components/Microservices (i.e., not a huge monolith); for teams who have solid JavaScript & Node.js knowledge; when sticking to Node.js narratives and spirit is desirable + +**Prefer Nest.js when** - It's desirable to design and code in OOP style; when the team is highly experienced with Java/Spring/Angular or similar; for large size app that can't be broken down (i.e. monolith) to autonomous component; for a team that lacks fundamental JavaScript/Node.js skills (not exclusively, this yet another consideration); when the decision-making overhead should be minimized; when the time to the first delivery is a critical factor + +

    + + +Get lost with express; Nest.js with Fastify, Fastify covers a lot, From d5aff223ef522226d25a4cec57b54b27c1db527c Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 11:46:01 +0300 Subject: [PATCH 1685/1795] Added nock --- README.md | 8 +- sections/testingandquality/citools.md | 51 ----------- .../mock-external-services.md | 87 +++++++++++++++++++ 3 files changed, 91 insertions(+), 55 deletions(-) delete mode 100644 sections/testingandquality/citools.md create mode 100644 sections/testingandquality/mock-external-services.md diff --git a/README.md b/README.md index c44c92eca..9b162177c 100644 --- a/README.md +++ b/README.md @@ -766,13 +766,13 @@ All statements above will return false if used with `===`

    -## ![✔] 4.12 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world) +## ![✔] 4.12 Mock responses of external HTTP services -**TL;DR:** Your continuous integration platform (CICD) will host all the quality tools (e.g. test, lint) so it should come with a vibrant ecosystem of plugins. [Jenkins](https://jenkins.io/) used to be the default for many projects as it has the biggest community along with a very powerful platform at the price of a complex setup that demands a steep learning curve. Nowadays, it has become much easier to set up a CI solution using SaaS tools like [CircleCI](https://circleci.com) and others. These tools allow crafting a flexible CI pipeline without the burden of managing the whole infrastructure. Eventually, it's a trade-off between robustness and speed - choose your side carefully +**TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production -**Otherwise:** Choosing some niche vendor might get you blocked once you need some advanced customization. On the other hand, going with Jenkins might burn precious time on infrastructure setup +**Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow -🔗 [**Read More: Choosing CI platform**](./sections/testingandquality/citools.md) +🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) ## ![✔] 4.13 Test your middlewares in isolation diff --git a/sections/testingandquality/citools.md b/sections/testingandquality/citools.md deleted file mode 100644 index 375e89fea..000000000 --- a/sections/testingandquality/citools.md +++ /dev/null @@ -1,51 +0,0 @@ -# Carefully choose your CI platform - -

    - -### One Paragraph Explainer - -The CI world used to be the flexibility of [Jenkins](https://jenkins.io/) vs the simplicity of SaaS vendors. The game is now changing as SaaS providers like [CircleCI](https://circleci.com/) and [Travis](https://travis-ci.org/) offer robust solutions including Docker containers with minimum setup time while Jenkins tries to compete on 'simplicity' segment as well. Though one can setup rich CI solution in the cloud, should it required to control the finest details Jenkins is still the platform of choice. The choice eventually boils down to which extent the CI process should be customized: free and setup free cloud vendors allow to run custom shell commands, custom docker images, adjust the workflow, run matrix builds and other rich features. However, if controlling the infrastructure or programming the CI logic using a formal programming language like Java is desired - Jenkins might still be the choice. Otherwise, consider opting for the simple and setup free cloud option - -

    - -### Code Example – a typical cloud CI configuration. Single .yml file and that's it - -```yaml -version: 2 -jobs: - build: - docker: - - image: circleci/node:4.8.2 - - image: mongo:3.4.4 - steps: - - checkout - - run: - name: Install npm wee - command: npm install - test: - docker: - - image: circleci/node:4.8.2 - - image: mongo:3.4.4 - steps: - - checkout - - run: - name: Test - command: npm test - - run: - name: Generate code coverage - command: './node_modules/.bin/nyc report --reporter=text-lcov' - - store_artifacts: - path: coverage - prefix: coverage - -``` - -### Circle CI - almost zero setup cloud CI - -![alt text](../../assets/images/circleci.png "API error handling") - -### Jenkins - sophisticated and robust CI - -![alt text](../../assets/images/jenkins_dashboard.png "API error handling") - -

    diff --git a/sections/testingandquality/mock-external-services.md b/sections/testingandquality/mock-external-services.md new file mode 100644 index 000000000..a31683d24 --- /dev/null +++ b/sections/testingandquality/mock-external-services.md @@ -0,0 +1,87 @@ +# Mock responses of external HTTP services + +

    + +### One Paragraph Explainer + +Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests + +

    + +### Code Example – a simple mock using nock + +```javascript +// Intercept requests for internal or 3rd party APIs and return a predefined response +beforeEach(() => { + nock("http://localhost/user/").get(`/1`).reply(200, { + id: 1, + name: "John", + }); +}); +``` + +### Code Example – simulating an important scenario inside the test + +```javascript +// Using an uncommon user id (7) and create a compatible interceptor +test("When the user does not exist, return http 404", async () => { + //Arrange + const orderToAdd = { + userId: 7, + productId: 2, + mode: "draft", + }; + + nock("http://localhost/user/").get(`/7`).reply(404, { + message: "User does not exist", + code: "nonExisting", + }); + + //Act + const orderAddResult = await axiosAPIClient.post("/order", orderToAdd); + + //Assert + expect(orderAddResult.status).toBe(404); +}); +``` + +### Code Example – preventing requests from going outside to the real-world + +```javascript +beforeAll(async () => { + // ... + // ️️️Ensure that this component is isolated by preventing unknown calls + nock.disableNetConnect(); + // Enable only requests for the API under test + nock.enableNetConnect("127.0.0.1"); +}); +``` + +### Code Example – ensuring that the outgoing request schema is correct + +```javascript +// ️️️Assert that the app called the mailer service appropriately with the right input +test("When order failed, send mail to admin", async () => { + //Arrange + // ... + let emailPayload; + nock("http://mailer.com") + .post("/send", (payload) => ((emailPayload = payload), true)) + .reply(202); + const orderToAdd = { + userId: 1, + productId: 2, + mode: "approved", + }; + + //Act + await axiosAPIClient.post("/order", orderToAdd); + + // ️️️Assert + expect(emailPayload).toMatchObject({ + subject: expect.any(String), + body: expect.any(String), + recipientAddress: expect.stringMatching(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/), + }); +}); +``` From 6b8c69014d54e2a8d0d798684f7af80824ec24c8 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 13:25:37 +0300 Subject: [PATCH 1686/1795] Added nock --- README.md | 16 +++++++++ .../images/The backend testing checklist.png | Bin 0 -> 189913 bytes sections/testingandquality/randomize-port.md | 31 ++++++++++++++++++ .../testingandquality/test-five-outcomes.md | 23 +++++++++++++ 4 files changed, 70 insertions(+) create mode 100644 assets/images/The backend testing checklist.png create mode 100644 sections/testingandquality/randomize-port.md create mode 100644 sections/testingandquality/test-five-outcomes.md diff --git a/README.md b/README.md index 9b162177c..91c64301d 100644 --- a/README.md +++ b/README.md @@ -782,6 +782,22 @@ All statements above will return false if used with `===` 🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) +## ![✔] 4.14 Specify a port in production, randomize in testing + +**TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port + +**Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default + +🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) + +## ![✔] 4.15 Test the five possible outcomes + +**TL;DR:** When testing a flow, ensure to cover the five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow, consider writing a test for each: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) + +**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue? there are typically more outcomes to consider than what meets the eye + +🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) +


    ⬆ Return to top

    diff --git a/assets/images/The backend testing checklist.png b/assets/images/The backend testing checklist.png new file mode 100644 index 0000000000000000000000000000000000000000..0b867d9cf1d5d3d0bc5dadbebfb595579e587bf0 GIT binary patch literal 189913 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 z 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 UoDs20?pE*~LaoC^cgE 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

    tECr&zJS@!TG9qmn(dNEAt}` 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 a&H5o&x6+v93}%T4@S20IvrRRc0Q?IE%un3_ literal 0 HcmV?d00001 diff --git a/sections/testingandquality/randomize-port.md b/sections/testingandquality/randomize-port.md new file mode 100644 index 000000000..28af118df --- /dev/null +++ b/sections/testingandquality/randomize-port.md @@ -0,0 +1,31 @@ +# Specify a port in production, randomize in testing + +

    + +### One Paragraph Explainer + +When writing component/integration tests, the web server should be started by the tests in the same process - this opens the door for many desirable testing features like mocking, coverage, and more. In a multi-process test runner, multiple web server instances will be opened. If these instances try to open the same port, they will collide. In testing only, let the server randomize a port to prevent collisions. This can easily achieved by providing an [ephemeral port](https://en.wikipedia.org/wiki/Ephemeral_port), the number zero, so the operating system will allocate an available port + +

    + +### Code Example – starting the web server with testing in-mind + +```javascript +// api-under-test.js +const initializeWebServer = async () => { + return new Promise((resolve, reject) => { + // Fixed port in production, a zero port (ephemeral) for testing + const webServerPort = process.env.PORT ? process.env.PORT : 0; + expressApp = express(); + connection = expressApp.listen(webServerPort, () => { + // No port + resolve(expressApp); + }); + }); +}; + +// test.js +beforeAll(async () => { + expressApp = await initializeWebServer(); // No port +}); +``` diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md new file mode 100644 index 000000000..daef207b6 --- /dev/null +++ b/sections/testingandquality/test-five-outcomes.md @@ -0,0 +1,23 @@ +# Test the five potential outcomes + +

    + +### One Paragraph Explainer + +When planning your tests, consider covering the five typical flow's outputs. When your test is triggering some action (e.g., API call), a reaction is happening, something meaningful occurs and calls for testing. Note that we don't care about how things work. Our focus is on outcomes, things that are noticeable from the outside and might affect the user. These outcomes/reactions can be put in 5 categories: + +• Response - The test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status + +• A new state - After invoking an action, some data is probably modified. For example, when updating a user - It might be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data' + +• External calls - After invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - Should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below + +• Message queues - The outcome of a flow might be a message in a queue. In our example application, once a new order was saved the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic + +• Observability - Some things must be monitored, like errors or remarkable business events. When a transaction fails, not only we expect the right response but also correct error handling and proper logging/metrics. This information goes directly to a very important user - The ops user (i.e., production SRE/admin). Testing error handler is not very straighforward - Many types of errors might get thrown, some errors should lead to process crash, and there are many other corners to cover. We plan to write the 📗 section on 'Observability and errors' soon + +

    + +### Example: The backend testing checklist + +![The backend testing checklist](../../assets/images/backend-testing-checklist.png "The backend testing checklist") From 409a7155f0df926e4aab758d42ee0d90952dd70b Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 15:04:13 +0300 Subject: [PATCH 1687/1795] More edits --- README.md | 76 +++++++++++++++++++++++++------------------------------ 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 91c64301d..933b2f1fa 100644 --- a/README.md +++ b/README.md @@ -28,11 +28,11 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin # Latest Best Practices and News -- **✨ 80,000 stars**: Blushing, surprised and proud! +- **🛰 2023 edition is released soon**: We're now writing the next edition, stay tuned? -- **🔖 New menu and tags**: Our menu is collapsible now and includes `#tags`. New visitors can read `#strategic` items first. Returning visitors can focus on `#new` content. Seniors can filter for `#advanced` items. Courtesy of the one and only [Rubek Joshi](https://github.com/rubek-joshi) +- **✨ 89,000 stars**: Blushing, surprised and proud! -- **👨‍👩‍👧‍👦 New family member!**: A new repository joins our family - [Node.js Integration Tests Best Practices ✨](https://github.com/testjavascript/nodejs-integration-tests-best-practices). It includes 40+ best practices for writing awesome and performant Node.js component tests +- **🔖 New menu and tags**: Our menu is collapsible now and includes `#tags`. New visitors can read `#strategic` items first. Returning visitors can focus on `#new` content. Seniors can filter for `#advanced` items. Courtesy of the one and only [Rubek Joshi](https://github.com/rubek-joshi) - **![FR](./assets/flags/FR.png) French translation!1! :** The latest translation that joins our international guide is French. Bienvenue @@ -55,11 +55,13 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    1. Project Architecture Practices (5) -  [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-components)
    -  [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-keep-the-web-layer-within-its-boundaries)
    +1.2 Layer your components with 3-tiers, keep the web layer within its boundaries +  [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-business-components)
    +  [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as npm packages](#-13-wrap-common-utilities-as-npm-packages)
    -  [1.4 Separate Express 'app' and 'server'](#-14-separate-express-app-and-server)
    -  [1.5 Use environment aware, secure and hierarchical config `#modified-recently`](#-15-use-environment-aware-secure-and-hierarchical-config)
    +  [1.4 Use environment aware, secure and hierarchical config `#updated`](#-15-use-environment-aware-secure-and-hierarchical-config)
    +  [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    +  [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)

    @@ -662,6 +664,12 @@ All statements above will return false if used with `===` # `4. Testing And Overall Quality Practices` +\_We have a dedicated guides for testing, see below. The practices here are a brief summary of these guides + +a. [JavaScript testing best practices](https://github.com/goldbergyoni/javascript-testing-best-practices) +b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +\_ + ## ![✔] 4.1 At the very least, write API (component) testing **TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc @@ -690,15 +698,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.4 Detect code issues with a linter - -**TL;DR:** Use a code linter to check the basic quality and detect anti-patterns early. Run it before any test and add it as a pre-commit git-hook to minimize the time needed to review and correct any issue. Also check [Section 3](#3-code-style-practices) on Code Style Practices - -**Otherwise:** You may let pass some anti-pattern and possible vulnerable code to your production environment. - -

    - -## ![✔] 4.5 Ensure Node.js version is unified +## ![✔] 4.4 Ensure Node.js version is unified **TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI Node.js definition) @@ -716,15 +716,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.6 Constantly inspect for vulnerable dependencies - -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities. This can get easily tamed using community and commercial tools such as 🔗 [npm audit](https://docs.npmjs.com/cli/audit) and 🔗 [snyk.io](https://snyk.io) that can be invoked from your CI on every build - -**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require to constantly follow online publications about new threats. Quite tedious - -

    - -## ![✔] 4.7 Tag your tests +## ![✔] 4.6 Tag your tests **TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' @@ -732,7 +724,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.8 Check your test coverage, it helps to identify wrong test patterns +## ![✔] 4.7 Check your test coverage, it helps to identify wrong test patterns **TL;DR:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold @@ -740,15 +732,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.9 Inspect for outdated packages - -**TL;DR:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version - -**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky - -

    - -## ![✔] 4.10 Use production-like environment for e2e testing +## ![✔] 4.8 Use production-like environment for e2e testing **TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) @@ -756,7 +740,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.11 Refactor regularly using static analysis tools +## ![✔] 4.9 Refactor regularly using static analysis tools **TL;DR:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). @@ -766,7 +750,7 @@ All statements above will return false if used with `===`

    -## ![✔] 4.12 Mock responses of external HTTP services +## ![✔] 4.10 Mock responses of external HTTP services **TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production @@ -774,7 +758,7 @@ All statements above will return false if used with `===` 🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) -## ![✔] 4.13 Test your middlewares in isolation +## ![✔] 4.11 Test your middlewares in isolation **TL;DR:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects @@ -782,7 +766,7 @@ All statements above will return false if used with `===` 🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) -## ![✔] 4.14 Specify a port in production, randomize in testing +## ![✔] 4.12 Specify a port in production, randomize in testing **TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port @@ -790,7 +774,7 @@ All statements above will return false if used with `===` 🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) -## ![✔] 4.15 Test the five possible outcomes +## ![✔] 4.13 Test the five possible outcomes **TL;DR:** When testing a flow, ensure to cover the five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow, consider writing a test for each: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) @@ -1295,7 +1279,17 @@ Also known as correlation id / transit id / tracing id / request id / request co 🔗 [**Read More: Avoid publishing secrets**](./sections/security/avoid_publishing_secrets.md) -## ![✔] 6.26. Import built-in modules using the 'node:' protocol +

    + +## ![✔] 6.26 Inspect for outdated packages + +**TL;DR:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version + +**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky + +

    + +## ![✔] 6.27. Import built-in modules using the 'node:' protocol From ef8648582aa92ff4dac41d757ac14627116c680d Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 15:06:28 +0300 Subject: [PATCH 1688/1795] More edits --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 933b2f1fa..4a1363e30 100644 --- a/README.md +++ b/README.md @@ -54,12 +54,10 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin 1. Project Architecture Practices (5) - -1.2 Layer your components with 3-tiers, keep the web layer within its boundaries   [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-business-components)
      [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as npm packages](#-13-wrap-common-utilities-as-npm-packages)
    -  [1.4 Use environment aware, secure and hierarchical config `#updated`](#-15-use-environment-aware-secure-and-hierarchical-config)
    +  [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
      [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    From ae54a431bc7405c8efa029fe46a4d51499ea8d42 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 15:10:03 +0300 Subject: [PATCH 1689/1795] More edits --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4a1363e30..4412752d6 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,9 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    - 1. Project Architecture Practices (5) + 1. Project Architecture Practices (6) +   [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-business-components)
      [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as npm packages](#-13-wrap-common-utilities-as-npm-packages)
    From b98b3400532cf45c6d1bb64a19ba2dc91e53816c Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 15:28:21 +0300 Subject: [PATCH 1690/1795] More edits --- README.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 4412752d6..0dab6f8ba 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin 1. Project Architecture Practices (6) - +   [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-business-components)
      [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as npm packages](#-13-wrap-common-utilities-as-npm-packages)
    @@ -101,6 +101,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [3.10 Use the === operator](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    +  [3.13 Avoid effects outside of functions #new](#-313-avoid-effects-outside-of-functions)
    @@ -112,16 +113,16 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [4.1 At the very least, write API (component) testing `#strategic`](#-41-at-the-very-least-write-api-component-testing)
      [4.2 Include 3 parts in each test name `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
    -  [4.4 Detect code issues with a linter](#-44-detect-code-issues-with-a-linter)
    +  [4.4 Ensure Node version is unified #new](#-44-ensure-node-version-is-unified)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
    -  [4.6 Constantly inspect for vulnerable dependencies](#-46-constantly-inspect-for-vulnerable-dependencies)
    -  [4.7 Tag your tests `#advanced`](#-47-tag-your-tests)
    -  [4.8 Check your test coverage, it helps to identify wrong test patterns](#-48-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
    -  [4.9 Inspect for outdated packages](#-49-inspect-for-outdated-packages)
    -  [4.10 Use production-like environment for e2e testing](#-410-use-production-like-environment-for-e2e-testing)
    -  [4.11 Refactor regularly using static analysis tools](#-411-refactor-regularly-using-static-analysis-tools)
    -  [4.12 Carefully choose your CI platform (Jenkins vs CircleCI vs Travis vs Rest of the world)](#-412-carefully-choose-your-ci-platform-jenkins-vs-circleci-vs-travis-vs-rest-of-the-world)
    -  [4.13 Test your middlewares in isolation](#-413-test-your-middlewares-in-isolation)
    +  [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
    +  [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
    +  [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
    +  [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
    +  [4.10 Mock responses of external HTTP services #advanced #new](#-410-mock-responses-of-external-http-services)
    +  [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
    +  [4.12 Specify a port in production, randomize in testing #new](#-412-specify-a-port-in-production-randomize-in-testing)
    +  [4.13 Test the five possible outcomes #strategic #new](#-413-test-the-five-possible-outcomes)
    @@ -441,12 +442,6 @@ especially if the cause of the abnormal behavior is inside of the missing functi 🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) -## ![✔] 3.2 Avoid effects outside of functions - -**TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code with effects inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns - -**Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored and might fail due to a lack of configuration data -

    ## ![✔] 3.2 Node.js specific plugins @@ -657,6 +652,14 @@ All statements above will return false if used with `===` 🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) +

    + +## ![✔] 3.13 Avoid effects outside of functions + +**TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns + +**Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data +


    ⬆ Return to top

    @@ -697,7 +700,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    -## ![✔] 4.4 Ensure Node.js version is unified +## ![✔] 4.4 Ensure Node version is unified **TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI Node.js definition) From ccd5c9d79d124eda66528de72f4f54bcf64646bc Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 8 May 2023 15:32:11 +0300 Subject: [PATCH 1691/1795] More edits --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0dab6f8ba..b208e7914 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,8 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [6.23. Avoid DOS attacks by explicitly setting when a process should crash `#advanced`](#-623-avoid-dos-attacks-by-explicitly-setting-when-a-process-should-crash)
      [6.24. Prevent unsafe redirects](#-624-prevent-unsafe-redirects)
      [6.25. Avoid publishing secrets to the npm registry](#-625-avoid-publishing-secrets-to-the-npm-registry)
    -  [6.26. Import built-in modules using the 'node:' protocol #new](#-626-import-built-in-modules-using-the-'node:'-protocol)
    +  [6.26. 6.26 Inspect for outdated packages](#-626-inspect-for-outdated-packages)
    +  [6.27. Import built-in modules using the 'node:' protocol #new](#-627-import-built-in-modules-using-the-node-protocol)
    From 723642168c5f44c1d972075ac7e911137c4beccd Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:06:53 +0300 Subject: [PATCH 1692/1795] Update README.md Co-authored-by: Nick Ribal --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b208e7914..6a0090b4f 100644 --- a/README.md +++ b/README.md @@ -667,7 +667,7 @@ All statements above will return false if used with `===` # `4. Testing And Overall Quality Practices` -\_We have a dedicated guides for testing, see below. The practices here are a brief summary of these guides +\_We have dedicated guides for testing, see below. The practices here are a brief summary of these guides a. [JavaScript testing best practices](https://github.com/goldbergyoni/javascript-testing-best-practices) b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) From 90a71145c9505824c45de6fcf5cc39d37c86506d Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:08:05 +0300 Subject: [PATCH 1693/1795] Update sections/projectstructre/typescript-considerations.md Co-authored-by: Nick Ribal --- sections/projectstructre/typescript-considerations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/projectstructre/typescript-considerations.md b/sections/projectstructre/typescript-considerations.md index c0831a1be..fcf881111 100644 --- a/sections/projectstructre/typescript-considerations.md +++ b/sections/projectstructre/typescript-considerations.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -TypeScript has won the community's hearts and is almost a standard for modern JavaScript apps. Compared with plain JS, It brings a much better coding ergonomics, facilitates intellisense even for historical libraries that were written with JavaScript and was proven to [prevent specific type of bugs](https://earlbarr.com/publications/typestudy.pdf). With that, if you look carefully under the hype, TypeScript actually brings two **mutual-exclusive** offerings to the table: type-safety and advanced design constructs like abstract classes, interfaces, namespaces and more. Many teams chose TypeScript for better type safety yet _unintentionally_, without any proper planning, use its for different purposes like OOP. These teams change their design style unintentionally due to the ['law of the instruments'](https://en.wikipedia.org/wiki/Law_of_the_instrument) — a cognitive bias that involves using the tooling in hand whether they are the right choice for the mission or not. In other words, if an 'abstract class' exists in the toolbox — developers will use it. If teams consciously opted for the coding techniques mentioned above — that's fair and legit. For others, positively consider coding with classic JavaScript, plain functions and objects, which are simply decorated with primitive types. The later option is likely to result in lower complexity +TypeScript has won the community's hearts and is almost a standard for modern JavaScript apps. Compared to plain JS, it brings much better coding ergonomics, facilitates editor code completions, even for historical libraries that were written with JavaScript and was proven to [prevent specific type of bugs](https://earlbarr.com/publications/typestudy.pdf). With that, if you look carefully under the hype, TypeScript actually brings two **mutually-exclusive** offerings to the table: type-safety and advanced design constructs like abstract classes, interfaces, namespaces and more. Many teams chose TypeScript for better type safety yet _unintentionally_, without any proper planning, use it for other purposes, such as OOP. These teams change their design style unintentionally due to the ['law of the instruments'](https://en.wikipedia.org/wiki/Law_of_the_instrument) — a cognitive bias that involves using the tooling in hand whether they are the right choice for the mission or not. In other words, if an 'abstract class' exists in the toolbox — developers will use it. If teams consciously opted for the coding techniques mentioned above — that's fair and legit. For others, positively consider coding with classic JavaScript, plain functions and objects, which are simply decorated with primitive types. The latter option is likely to result in lower complexity

    From 6bddf76f6a750c4bf3b73586b075f848fb576f64 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:08:52 +0300 Subject: [PATCH 1694/1795] Update sections/testingandquality/mock-external-services.md Co-authored-by: Nick Ribal --- sections/testingandquality/mock-external-services.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sections/testingandquality/mock-external-services.md b/sections/testingandquality/mock-external-services.md index a31683d24..6def85708 100644 --- a/sections/testingandquality/mock-external-services.md +++ b/sections/testingandquality/mock-external-services.md @@ -4,7 +4,8 @@ ### One Paragraph Explainer -Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests +Isolate the component under test by intercepting any outgoing HTTP request and providing the desired response so the collaborator HTTP API won't get hit. Nock is a great tool for this mission as it provides a convenient syntax for defining external services behavior. Isolation is a must to prevent noise and slow performance but mostly to simulate various scenarios and responses - A good flight simulator is not about painting clear blue sky rather bringing safe storms and chaos. This is reinforced in a Microservice architecture where the focus should always be on a single component without involving the rest of the world. Though it's possible to simulate external service behavior using test doubles (mocking), it's preferable not to touch the deployed code and act on the network level to keep the tests pure black-box. The downside of isolation is not detecting when the collaborator component changes and not realizing misunderstandings between the two services - Make sure to compensate for this using a few contract or E2E tests. +

    From 8c9dca852323f0757ba85c2cdfb720a5ed17226b Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:09:01 +0300 Subject: [PATCH 1695/1795] Update sections/testingandquality/test-five-outcomes.md Co-authored-by: Nick Ribal --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index daef207b6..6acae3a42 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -8,7 +8,7 @@ When planning your tests, consider covering the five typical flow's outputs. Whe • Response - The test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status -• A new state - After invoking an action, some data is probably modified. For example, when updating a user - It might be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data' +• A new state - after invoking an action, some data is probably modified. For example, when updating a user, it may be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data'. • External calls - After invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - Should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below From a905485ee3f9691ac5d509402fe1440ad1755edf Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:09:10 +0300 Subject: [PATCH 1696/1795] Update sections/testingandquality/test-five-outcomes.md Co-authored-by: Nick Ribal --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index 6acae3a42..fc8b4c20c 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -6,7 +6,7 @@ When planning your tests, consider covering the five typical flow's outputs. When your test is triggering some action (e.g., API call), a reaction is happening, something meaningful occurs and calls for testing. Note that we don't care about how things work. Our focus is on outcomes, things that are noticeable from the outside and might affect the user. These outcomes/reactions can be put in 5 categories: -• Response - The test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status +• Response - the test invokes an action (e.g., via API) and gets a response. It's now concerned with checking the response data correctness, schema, and HTTP status. • A new state - after invoking an action, some data is probably modified. For example, when updating a user, it may be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data'. From 1157953160a81aa261f9433df7128b5dfd9c91d7 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:09:25 +0300 Subject: [PATCH 1697/1795] Update sections/testingandquality/test-five-outcomes.md Co-authored-by: Nick Ribal --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index fc8b4c20c..2b1a324b3 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -10,7 +10,7 @@ When planning your tests, consider covering the five typical flow's outputs. Whe • A new state - after invoking an action, some data is probably modified. For example, when updating a user, it may be that the new data was not saved. Commonly and mistakenly, testers check only the response and not whether the data is updated correctly. Testing data and databases raises multiple interesting challenges that are greatly covered below in the 📗 section 'Dealing with data'. -• External calls - After invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - Should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below +• External calls - after invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below. • Message queues - The outcome of a flow might be a message in a queue. In our example application, once a new order was saved the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic From 501cfde3b303718b6c39596c7f77d79869312eb2 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:09:37 +0300 Subject: [PATCH 1698/1795] Update sections/testingandquality/test-five-outcomes.md Co-authored-by: Nick Ribal --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index 2b1a324b3..e3ed84ab5 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -12,7 +12,7 @@ When planning your tests, consider covering the five typical flow's outputs. Whe • External calls - after invoking an action, the app might call an external component via HTTP or any other transport. For example, a call to send SMS, email or charge a credit card. Anything that goes outside and might affect the user - should be tested. Testing integrations is a broad topic which is discussed in the 📗 section 'Testing integrations' below. -• Message queues - The outcome of a flow might be a message in a queue. In our example application, once a new order was saved the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic +• Message queues - the outcome of a flow might be a message in a queue. In our example application, once a new order was saved, the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations, only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic. • Observability - Some things must be monitored, like errors or remarkable business events. When a transaction fails, not only we expect the right response but also correct error handling and proper logging/metrics. This information goes directly to a very important user - The ops user (i.e., production SRE/admin). Testing error handler is not very straighforward - Many types of errors might get thrown, some errors should lead to process crash, and there are many other corners to cover. We plan to write the 📗 section on 'Observability and errors' soon From e803dab3b87d4df57c755635fc7b3e2965348e0e Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 11:10:02 +0300 Subject: [PATCH 1699/1795] Update sections/testingandquality/test-five-outcomes.md Co-authored-by: Nick Ribal --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index e3ed84ab5..450a11b72 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -14,7 +14,7 @@ When planning your tests, consider covering the five typical flow's outputs. Whe • Message queues - the outcome of a flow might be a message in a queue. In our example application, once a new order was saved, the app puts a message in some MQ product. Now other components can consume this message and continue the flow. This is very similar to testing integrations, only working with message queues is different technically and tricky. The 📗 section 'Message Queues' below delve into this topic. -• Observability - Some things must be monitored, like errors or remarkable business events. When a transaction fails, not only we expect the right response but also correct error handling and proper logging/metrics. This information goes directly to a very important user - The ops user (i.e., production SRE/admin). Testing error handler is not very straighforward - Many types of errors might get thrown, some errors should lead to process crash, and there are many other corners to cover. We plan to write the 📗 section on 'Observability and errors' soon +• Observability - some things must be monitored, like errors or remarkable business events. When a transaction fails, not only do we expect the right response but also the correct error handling and proper logging/metrics. This information goes directly to a very important user - the ops user (i.e., production SRE/admin). Testing error handlers isn't very straightforward because many types of errors might get thrown, where some errors should lead to process crashing, and there are many other details to cover. We plan to write the 📗 section on 'Observability and errors' soon.

    From bb5dabdc35f8aa888982c54d14205d0ad6667b00 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 12:57:49 +0300 Subject: [PATCH 1700/1795] Various fixes --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 6a0090b4f..250a57be6 100644 --- a/README.md +++ b/README.md @@ -227,7 +227,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin ## ![✔] 1.1 Structure your solution by business components -**TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like user-component, order-component, etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a more granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo +**TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo ```bash my-system @@ -240,7 +240,7 @@ my-system │ ├─ authenticator ``` -**Otherwise:** when artifacts from various modules/topics are mixed together, there are great chances of a tightly-coupled 'spaghetti' system. For example, when 'module-a' controller might call 'module-b service', every code change might affect any other module and file. With this approach, developers who code new features struggle to realize the scope and impact of their change. Consequently, they fear breaking other modules, and each deployment becomes slower and riskier +**Otherwise:** when artifacts from various modules/topics are mixed together, there are great chances of a tightly-coupled 'spaghetti' system. For example, in an architecture where 'module-a controller' might call 'module-b service', there are no clear modularity borders - every code change might affect anything else. With this approach, developers who code new features struggle to realize the scope and impact of their change. Consequently, they fear breaking other modules, and each deployment becomes slower and riskier 🔗 [**Read More: structure by components**](./sections/projectstructre/breakintcomponents.md) @@ -261,7 +261,7 @@ my-system │ ├─ data-access # DB calls w/o ORM ``` -**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access the the logic by other clients like testing code, scheduled jobs, message queues, etc +**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc 🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) @@ -287,7 +287,7 @@ my-system ## ![✔] 1.5 Consider all the consequences when choosing the main framework -**TL;DR:** When building apps and APIs using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: Nest.js, Fastify, express, and Koa. Click read more for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller deployment units. Fastify is our recommendation for apps with reasonably-sized components that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) +**TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) **Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns @@ -295,7 +295,7 @@ my-system ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully -**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly for simple types only +**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features solely when absolutely necessary **Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time @@ -703,7 +703,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.4 Ensure Node version is unified -**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI Node.js definition) +**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file). **Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed @@ -779,9 +779,9 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.13 Test the five possible outcomes -**TL;DR:** When testing a flow, ensure to cover the five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow, consider writing a test for each: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +**TL;DR:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue? there are typically more outcomes to consider than what meets the eye +**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) 🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) @@ -1308,7 +1308,7 @@ For example: import { createServer } from "node:http"; ``` -This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to the official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) +This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to a well-trusted official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) **Otherwise:** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them From 004783441d1d527d97ae50ec43d8ffb24097b036 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 13:11:59 +0300 Subject: [PATCH 1701/1795] Various fixes --- .../lint-and-generate-html-from-markdown.yml | 16 ++++++++-------- README.md | 2 +- .operations/package.json => package.json | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) rename .operations/package.json => package.json (92%) diff --git a/.github/workflows/lint-and-generate-html-from-markdown.yml b/.github/workflows/lint-and-generate-html-from-markdown.yml index 721c4da99..9c62c5b4b 100644 --- a/.github/workflows/lint-and-generate-html-from-markdown.yml +++ b/.github/workflows/lint-and-generate-html-from-markdown.yml @@ -18,13 +18,13 @@ jobs: NODE_ENV: test steps: - - name: Checkout - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v3 - - name: Setup Node.js environment - uses: actions/setup-node@v3 - with: - node-version: '18' + - name: Setup Node.js environment + uses: actions/setup-node@v3 + with: + node-version: "18" - - run: npm install - - run: npm run lint + - run: npm install + - run: npm run lint diff --git a/README.md b/README.md index 250a57be6..dc3f77459 100644 --- a/README.md +++ b/README.md @@ -703,7 +703,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.4 Ensure Node version is unified -**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file). +**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) **Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed diff --git a/.operations/package.json b/package.json similarity index 92% rename from .operations/package.json rename to package.json index 80c938f0a..66bf9d9a0 100644 --- a/.operations/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "[✔]: assets/images/checkbox-small-blue.png", "main": "gen-html.js", "scripts": { - "lint": "markdownlint ../README*.md" + "lint": "markdownlint ./README*.md" }, "repository": { "type": "git", From f6ba725e0a9db45dd5e363092d99b316c9540564 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 15:26:18 +0300 Subject: [PATCH 1702/1795] Error handling section --- README.md | 36 ++++---- sections/errorhandling/testingerrorflows.md | 91 ++++++++++----------- 2 files changed, 62 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index dc3f77459..3ad8dbb9e 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,12 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [2.1 Use Async-Await or promises for async error handling](#-21-use-async-await-or-promises-for-async-error-handling)
    -  [2.2 Use only the built-in Error object `#strategic`](#-22-use-only-the-built-in-error-object)
    -  [2.3 Distinguish operational vs programmer errors `#strategic`](#-23-distinguish-operational-vs-programmer-errors)
    +  [2.2 Extend the built-in Error object `#strategic #updated`](#-22-extend-the-built-in-error-object)
    +  [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    -  [2.5 Document API errors using Swagger or GraphQL `#modified-recently`](#-25-document-api-errors-using-swagger-or-graphql)
    +  [2.5 Document API errors using OpenAPI or GraphQL `#updated`](#-25-document-api-errors-using-openapi-or -graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
    -  [2.7 Use a mature logger to increase error visibility](#-27-use-a-mature-logger-to-increase-error-visibility)
    +  [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 Test error flows using your favorite test framework](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 Catch unhandled promise rejections `#modified-recently`](#-210-catch-unhandled-promise-rejections)
    @@ -309,7 +309,7 @@ my-system ## ![✔] 2.1 Use Async-Await or promises for async error handling -**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using a reputable promise library or async-await instead which enables a much more compact and familiar code syntax like try-catch +**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch **Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns @@ -317,9 +317,9 @@ my-system

    -## ![✔] 2.2 Use only the built-in Error object +## ![✔] 2.2 Extend the built-in Error object -**TL;DR:** Many throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Whether you reject a promise, throw an exception or emit an error – using only the built-in Error object (or an object that extends the built-in Error object) will increase uniformity and prevent loss of information. There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) +**TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! @@ -327,11 +327,11 @@ my-system

    -## ![✔] 2.3 Distinguish operational vs programmer errors +## ![✔] 2.3 Distinguish catastrophic errors from operational errors -**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, programmer error (e.g. trying to read an undefined variable) refers to unknown code failures that dictate to gracefully restart the application +**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application -**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context +**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context 🔗 [**Read More: operational vs programmer error**](./sections/errorhandling/operationalvsprogrammererror.md) @@ -339,7 +339,7 @@ my-system ## ![✔] 2.4 Handle errors centrally, not within a middleware -**TL;DR:** Error handling logic such as mail to admin and logging should be encapsulated in a dedicated and centralized object that all endpoints (e.g. Express middleware, cron jobs, unit-testing) call when an error comes in +**TL;DR:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in **Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors @@ -347,9 +347,9 @@ my-system

    -## ![✔] 2.5 Document API errors using Swagger or GraphQL +## ![✔] 2.5 Document API errors using OpenAPI or GraphQL -**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like Swagger. If you're using GraphQL, you can utilize your schema and comments as well. +**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well **Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) @@ -359,7 +359,7 @@ my-system ## ![✔] 2.6 Exit the process gracefully when a stranger comes to town -**TL;DR:** When an unknown error occurs (a developer error, see best practice 2.3) - there is uncertainty about the application healthiness. Common practice suggests restarting the process carefully using a process management tool like [Forever](https://www.npmjs.com/package/forever) or [PM2](http://pm2.keymetrics.io/) +**TL;DR:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart **Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily @@ -367,9 +367,9 @@ my-system

    -## ![✔] 2.7 Use a mature logger to increase error visibility +## ![✔] 2.7 Use a mature logger to increase errors visibility -**TL;DR:** A set of mature logging tools like [Pino](https://github.com/pinojs/pino) or [Log4js](https://www.npmjs.com/package/log4js), will speed-up error discovery and understanding. So forget about console.log +**TL;DR:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late @@ -379,7 +379,7 @@ my-system ## ![✔] 2.8 Test error flows using your favorite test framework -**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. Testing frameworks like Mocha & Chai can handle this easily (see code examples within the "Gist popup") +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) **Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling @@ -409,7 +409,7 @@ my-system ## ![✔] 2.11 Fail fast, validate arguments using a dedicated library -**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a very cool helper library like [ajv](https://www.npmjs.com/package/ajv) and [Joi](https://www.npmjs.com/package/joi) +**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) **Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? diff --git a/sections/errorhandling/testingerrorflows.md b/sections/errorhandling/testingerrorflows.md index 552ca1f53..ac03f4146 100644 --- a/sections/errorhandling/testingerrorflows.md +++ b/sections/errorhandling/testingerrorflows.md @@ -10,72 +10,69 @@ Testing ‘happy’ paths is no better than testing failures. Good testing code Javascript ```javascript -describe('Facebook chat', () => { - it('Notifies on new chat message', () => { +describe("Facebook chat", () => { + it("Notifies on new chat message", () => { const chatService = new chatService(); chatService.participants = getDisconnectedParticipants(); - expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); + expect(chatService.sendMessage.bind({ message: "Hi" })).to.throw(ConnectionError); }); }); ``` + +### Code example: ensuring API returns the right HTTP error code and log properly +
    -Typescript +Javascript -```typescript -describe('Facebook chat', () => { - it('Notifies on new chat message', () => { - const chatService = new chatService(); - chatService.participants = getDisconnectedParticipants(); - expect(chatService.sendMessage.bind({ message: 'Hi' })).to.throw(ConnectionError); +```javascript +test("When exception is throw during request, Then logger reports the mandatory fields", async () => { + //Arrange + const orderToAdd = { + userId: 1, + productId: 2, + }; + + sinon + .stub(OrderRepository.prototype, "addOrder") + .rejects(new AppError("saving-failed", "Order could not be saved", 500)); + const loggerDouble = sinon.stub(logger, "error"); + + //Act + const receivedResponse = await axiosAPIClient.post("/order", orderToAdd); + + //Assert + expect(receivedResponse.status).toBe(500); + expect(loggerDouble.lastCall.firstArg).toMatchObject({ + name: "saving-failed", + status: 500, + stack: expect.any(String), + message: expect.any(String), }); }); ``` +
    -### Code example: ensuring API returns the right HTTP error code +### Code example: ensuring that are uncaught exceptions are handled as well
    Javascript ```javascript -it('Creates new Facebook group', () => { - const invalidGroupInfo = {}; - return httpRequest({ - method: 'POST', - uri: 'facebook.com/api/groups', - resolveWithFullResponse: true, - body: invalidGroupInfo, - json: true - }).then((response) => { - expect.fail('if we were to execute the code in this block, no error was thrown in the operation above') - }).catch((response) => { - expect(400).to.equal(response.statusCode); - }); -}); -``` -
    +test("When unhandled exception is throw, Then the logger reports correctly", async () => { + //Arrange + await api.startWebServer(); + const loggerDouble = sinon.stub(logger, "error"); + const errorToThrow = new Error("An error that wont be caught 😳"); -
    -Typescript - -```typescript -it('Creates new Facebook group', async () => { - let invalidGroupInfo = {}; - try { - const response = await httpRequest({ - method: 'POST', - uri: 'facebook.com/api/groups', - resolveWithFullResponse: true, - body: invalidGroupInfo, - json: true - }) - // if we were to execute the code in this block, no error was thrown in the operation above - expect.fail('The request should have failed') - } catch(response) { - expect(400).to.equal(response.statusCode); - } + //Act + process.emit("uncaughtException", errorToThrow); + + // Assert + expect(loggerDouble.calledWith(errorToThrow)); }); ``` -
    \ No newline at end of file + + From f8b1810fa75b45a31f43a7c62212584dea1b4fd3 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 9 May 2023 21:40:51 +0300 Subject: [PATCH 1703/1795] Code quality section --- README.md | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3ad8dbb9e..e2f130b0b 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [3.10 Use the === operator](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    -  [3.13 Avoid effects outside of functions #new](#-313-avoid-effects-outside-of-functions)
    +  [3.13 Avoid effects outside of functions `#new`](#-313-avoid-effects-outside-of-functions)
    @@ -113,15 +113,15 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [4.1 At the very least, write API (component) testing `#strategic`](#-41-at-the-very-least-write-api-component-testing)
      [4.2 Include 3 parts in each test name `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
    -  [4.4 Ensure Node version is unified #new](#-44-ensure-node-version-is-unified)
    +  [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
      [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
      [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
    -  [4.10 Mock responses of external HTTP services #advanced #new](#-410-mock-responses-of-external-http-services)
    +  [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
    -  [4.12 Specify a port in production, randomize in testing #new](#-412-specify-a-port-in-production-randomize-in-testing)
    +  [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
      [4.13 Test the five possible outcomes #strategic #new](#-413-test-the-five-possible-outcomes)
    @@ -437,7 +437,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi ## ![✔] 3.1 Use ESLint -**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) and [beautify](https://www.npmjs.com/package/js-beautify) are more powerful in formatting the fix and work in conjunction with ESLint +**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint **Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style @@ -445,9 +445,9 @@ especially if the cause of the abnormal behavior is inside of the missing functi

    -## ![✔] 3.2 Node.js specific plugins +## ![✔] 3.2 Use Node.js eslint extension plugins -**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security) +**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules **Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early @@ -588,22 +588,27 @@ function doSomething() {

    -## ![✔] 3.9 Require modules by folders, as opposed to the files directly +## ![✔] 3.9 Set an explicit entry point per module/folder -**TL;DR:** When developing a module/library in a folder, place an index.js file that exposes the module's internals so every consumer will pass through it. This serves as an 'interface' to your module and eases future changes without breaking the contract +**TL;DR:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality -**Otherwise:** Changing the internal structure of files or the signature may break the interface with clients +**Otherwise:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract -### 3.9 Code example +### 3.9 Code example - avoid coupling the client to the module structure ```javascript -// Do -module.exports.SMSProvider = require("./SMSProvider"); -module.exports.SMSNumberResolver = require("./SMSNumberResolver"); +// Avoid: client has deep familiarity with the internals -// Avoid -module.exports.SMSProvider = require("./SMSProvider/SMSProvider.js"); -module.exports.SMSNumberResolver = require("./SMSNumberResolver/SMSNumberResolver.js"); +// Client code +const SMSWithMedia = require("./SMSProvider/providers/media/media-provider.js"); + +// Better: explicitly export the public functions + +//index.js, module code +module.exports.SMSWithMedia = require("./SMSProvider/providers/media/media-provider.js"); + +// Client code +const { SMSWithMedia } = require("./SMSProvider"); ```

    @@ -667,7 +672,7 @@ All statements above will return false if used with `===` # `4. Testing And Overall Quality Practices` -\_We have dedicated guides for testing, see below. The practices here are a brief summary of these guides +\_We have dedicated guides for testing, see below. The best practices list here is a brief summary of these guides a. [JavaScript testing best practices](https://github.com/goldbergyoni/javascript-testing-best-practices) b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) From 235516cc9f8f223b611a83bd0f6d31f3015fb063 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 10 May 2023 11:26:38 +0300 Subject: [PATCH 1704/1795] Production practices --- README.md | 38 +++++++-------- sections/production/lockdependencies.md | 64 ++++++++----------------- sections/production/productioncode.md | 17 +++---- 3 files changed, 48 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index e2f130b0b..24d97b520 100644 --- a/README.md +++ b/README.md @@ -806,7 +806,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    -## ![✔] 5.2. Increase transparency using smart logging +## ![✔] 5.2. Increase the observability using smart logging **TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted @@ -818,7 +818,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy -**TL;DR:** Node is awfully bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use ‘real’ middleware services like nginx, HAproxy or cloud vendor services instead +**TL;DR:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead **Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly @@ -828,7 +828,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.4. Lock dependencies -**TL;DR:** Your code must be identical across all environments, but amazingly npm lets dependencies drift across environments by default – when you install packages at various environments it tries to fetch packages’ latest patch version. Overcome this by using npm config files, .npmrc, that tell each environment to save the exact (not the latest) version of each package. Alternatively, for finer grained control use `npm shrinkwrap`. \*Update: as of NPM5, dependencies are locked by default. The new package manager in town, Yarn, also got us covered by default +**TL;DR:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code @@ -838,7 +838,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.5. Guard process uptime using the right tool -**TL;DR:** The process must go on and get restarted upon failures. For simple scenarios, process management tools like PM2 might be enough but in today's ‘dockerized’ world, cluster management tools should be considered as well +**TL;DR:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location **Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos @@ -848,7 +848,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.6. Utilize all CPU cores -**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs – For small-medium apps you may use Node Cluster or PM2. For a larger app consider replicating the process using some Docker cluster (e.g. K8S, ECS) or deployment scripts that are based on Linux init system (e.g. systemd) +**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) **Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) @@ -866,9 +866,9 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    -## ![✔] 5.8. Discover errors and downtime using APM products +## ![✔] 5.8. Discover the unknowns using APM products -**TL;DR:** Application monitoring and performance products (a.k.a. APM) proactively gauge codebase and API so they can auto-magically go beyond traditional monitoring and measure the overall user-experience across services and tiers. For example, some APM products can highlight a transaction that loads too slow on the end-user's side while suggesting the root cause +**TL;DR:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX @@ -878,7 +878,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.9. Make your code production-ready -**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click Gist below) +**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') **Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written @@ -898,7 +898,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.11. Get your frontend assets out of Node -**TL;DR:** Serve frontend content using dedicated middleware (nginx, S3, CDN) because Node performance really gets hurt when dealing with many static files due to its single-threaded model +**TL;DR:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering **Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content @@ -906,9 +906,9 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    -## ![✔] 5.12. Be stateless, kill your servers almost every day +## ![✔] 5.12. Strive to be stateless -**TL;DR:** Store any type of data (e.g. user sessions, cache, uploaded files) within external data stores. Consider ‘killing’ your servers periodically or use ‘serverless’ platform (e.g. AWS Lambda) that explicitly enforces a stateless behavior +**TL;DR:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically **Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server @@ -918,7 +918,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.13. Use tools that automatically detect vulnerabilities -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately **Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious @@ -928,9 +928,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.14. Assign a transaction id to each log statement -Also known as correlation id / transit id / tracing id / request id / request context / etc. - -**TL;DR:** Assign the same identifier, transaction-id: {some value}, to each log entry within a single request. Then when inspecting errors in logs, easily conclude what happened before and after. Until version 14 of Node, this was not easy to achieve due to Node's async nature, but since AsyncLocalStorage came to town, this became possible and easy than ever. see code examples inside +**TL;DR:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside **Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue @@ -940,9 +938,9 @@ Also known as correlation id / transit id / tracing id / request id / request co ## ![✔] 5.15. Set `NODE_ENV=production` -**TL;DR:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – many npm packages determine the current environment and optimize their code for production +**TL;DR:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production -**Otherwise:** Omitting this simple property might greatly degrade performance. For example, when using Express for server-side rendering omitting `NODE_ENV` makes it slower by a factor of three! +**Otherwise:** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering 🔗 [**Read More: Set NODE_ENV=production**](./sections/production/setnodeenv.md) @@ -966,11 +964,11 @@ Also known as correlation id / transit id / tracing id / request id / request co

    -## ![✔] 5.18. Don't route logs within the app +## ![✔] 5.18. Log to stdout, avoid specifying log destination within the app **TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). -**Otherwise:** Application handling log routing === hard to scale, loss of logs, poor separation of concerns +**Otherwise:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive 🔗 [**Read More: Log Routing**](./sections/production/logrouting.md) @@ -978,7 +976,7 @@ Also known as correlation id / transit id / tracing id / request id / request co ## ![✔] 5.19. Install your packages with `npm ci` -**TL;DR:** You have to be sure that production code uses the exact version of the packages you have tested it with. Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Using this command is recommended in automated environments such as continuous integration pipelines. +**TL;DR:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. diff --git a/sections/production/lockdependencies.md b/sections/production/lockdependencies.md index 1698c9bbb..61800062f 100644 --- a/sections/production/lockdependencies.md +++ b/sections/production/lockdependencies.md @@ -4,9 +4,7 @@ ### One Paragraph Explainer -Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production npm might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using npm config files and the argument ```–save-exact=true``` instructs npm to refer to the *exact* same version that was installed so the next time you run ```npm install``` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. An alternative and popular approach is using a `.shrinkwrap` file (easily generated using npm) that states exactly which packages and versions should be installed so no environment can get tempted to fetch newer versions than expected. - -* **Update:** as of npm 5, dependencies are locked automatically using .shrinkwrap. Yarn, an emerging package manager, also locks down dependencies by default. +Your code depends on many external packages, let’s say it ‘requires’ and use momentjs-2.1.4, then by default when you deploy to production npm might fetch momentjs 2.1.5 which unfortunately brings some new bugs to the table. Using npm config files and the argument `–save-exact=true` instructs npm to refer to the _exact_ same version that was installed so the next time you run `npm install` (in production or within a Docker container you plan to ship forward for testing) the same dependent version will be fetched. Due to this, starting from npm version 5 a package-lock.json file is generated in every install. This lock file pins all the dependencies and child dependencies versions. When the file is committed, any future install the gets a copy of the app will install the same dependencies version

    @@ -19,51 +17,31 @@ save-exact:true

    -### Code example: shrinkwrap.json file that distills the exact dependency tree - -```json -{ - "name": "A", - "dependencies": { - "B": { - "version": "0.0.1", - "dependencies": { - "C": { - "version": "0.1.0" - } - } - } - } -} -``` - -

    - ### Code example: npm 5 dependencies lock file – package-lock.json ```json { - "name": "package-name", - "version": "1.0.0", - "lockfileVersion": 1, - "dependencies": { - "cacache": { - "version": "9.2.6", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", - "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" - }, - "duplexify": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", - "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", - "dependencies": { - "end-of-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", - "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" - } - } + "name": "package-name", + "version": "1.0.0", + "lockfileVersion": 1, + "dependencies": { + "cacache": { + "version": "9.2.6", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-9.2.6.tgz", + "integrity": "sha512-YK0Z5Np5t755edPL6gfdCeGxtU0rcW/DBhYhYVDckT+7AFkCCtedf2zru5NRbBLFk6e7Agi/RaqTOAfiaipUfg==" + }, + "duplexify": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", + "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", + "dependencies": { + "end-of-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", + "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=" } + } } + } } ``` diff --git a/sections/production/productioncode.md b/sections/production/productioncode.md index a86047312..6fb624a6c 100644 --- a/sections/production/productioncode.md +++ b/sections/production/productioncode.md @@ -6,11 +6,12 @@ Following is a list of development tips that greatly affect the production maintenance and stability: -* The twelve-factor guide – Get familiar with the [Twelve factors](https://12factor.net/) guide -* Be stateless – Save no data locally on a specific web server (see separate bullet – ‘Be Stateless’) -* Cache – Utilize cache heavily, yet never fail because of cache mismatch -* Test memory – gauge memory usage and leaks as part your development flow, tools such as ‘memwatch’ can greatly facilitate this task -* Name functions – Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will provide memory usage per method name -* Use CI tools – Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference errors and undefined variables. Use –trace-sync-io to identify code that uses synchronous APIs (instead of the async version) -* Log wisely – Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such as Elastic can search upon those properties (see separate bullet – ‘Increase visibility using smart logs’). Also, include transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate bullet – ‘Include Transaction-ID’) -* Error management – Error handling is the Achilles’ heel of Node.js production sites – many Node processes are crashing because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy is absolutely critical, read here my [error handling best practices](http://goldbergyoni.com/checklist-best-practices-of-node-js-error-handling/) +- The twelve-factor guide – Get familiar with the [Twelve factors](https://12factor.net/) guide +- Be stateless – Save no data locally on a specific web server (see separate bullet – ‘Be Stateless’) +- Cache – Utilize cache heavily, yet never fail because of cache mismatch +- Test memory – gauge memory usage and leaks as part your development flow, tools such as ‘memwatch’ can greatly facilitate this task +- Name functions – Minimize the usage of anonymous functions (i.e. inline callback) as a typical memory profiler will provide memory usage per method name +- Use CI tools – Use CI tool to detect failures before sending to production. For example, use ESLint to detect reference errors and undefined variables. Use –trace-sync-io to identify code that uses synchronous APIs (instead of the async version) +- Log wisely – Include in each log statement contextual information, hopefully in JSON format so log aggregators tools such as Elastic can search upon those properties (see separate bullet – ‘Increase visibility using smart logs’). Also, include transaction-id that identifies each request and allows to correlate lines that describe the same transaction (see separate bullet – ‘Include Transaction-ID’) +- Test like production - Make developers machine quite close to the production infrastructure (e.g., with Docker-Compose). Avoid if/else clauses in testing that check if we're in testing environment but rather run the same code always +- Error management – Error handling is the Achilles’ heel of Node.js production sites – many Node processes are crashing because of minor errors while others hang on alive in a faulty state instead of crashing. Setting your error handling strategy is absolutely critical, read here my [error handling best practices](http://goldbergyoni.com/checklist-best-practices-of-node-js-error-handling/) From 24c207d404fbe62c571ea1cbaa2962ff51b85e28 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 10 May 2023 18:30:13 +0300 Subject: [PATCH 1705/1795] Production practices --- README.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 24d97b520..a64b2daf2 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [6.24. Prevent unsafe redirects](#-624-prevent-unsafe-redirects)
      [6.25. Avoid publishing secrets to the npm registry](#-625-avoid-publishing-secrets-to-the-npm-registry)
      [6.26. 6.26 Inspect for outdated packages](#-626-inspect-for-outdated-packages)
    -  [6.27. Import built-in modules using the 'node:' protocol #new](#-627-import-built-in-modules-using-the-node-protocol)
    +  [6.27. Import built-in modules using the 'node:' protocol `#new`](#-627-import-built-in-modules-using-the-node-protocol)
    @@ -267,11 +267,11 @@ my-system

    -## ![✔] 1.3 Wrap common utilities as npm packages +## ![✔] 1.3 Wrap common utilities as packages, consider publishing -**TL;DR:** In a large app that constitutes a large codebase, cross-cutting-concern utilities like a logger, encryption and alike, should be wrapped by your code and exposed as private npm packages. This allows sharing them among multiple codebases and projects +**TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry -**Otherwise:** You'll have to invent your deployment and the dependency wheel +**Otherwise:** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface 🔗 [**Read More: Structure by feature**](./sections/projectstructre/wraputilities.md) @@ -279,9 +279,9 @@ my-system ## ![✔] 1.4 Use environment aware, secure and hierarchical config -**TL;DR:** A perfect and flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability. There are a few packages that can help tick most of those boxes like [rc](https://www.npmjs.com/package/rc), [nconf](https://www.npmjs.com/package/nconf), [config](https://www.npmjs.com/package/config), and [convict](https://www.npmjs.com/package/convict). +**TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others -**Otherwise:** Failing to satisfy any of the config requirements will simply bog down the development or DevOps team. Probably both +**Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state 🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) @@ -798,7 +798,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.1. Monitoring -**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. Click ‘The Gist’ below for an overview of the solutions +**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions **Otherwise:** Failure === disappointed customers. Simple @@ -1386,9 +1386,11 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.2. Bootstrap using `node` command, avoid `npm start` -**TL;DR:** use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-processes, signal handling, graceful shutdown and having zombie processes. +**TL;DR:** Use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-processes, signal handling, graceful shutdown and having zombie processes -**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data. +Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm/changelog#706-2020-10-27) to pass signals. We follow and will update accordingly + +**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data [**Read More: Bootstrap container using node command, avoid npm start**](./sections/docker/bootstrap-using-node.md) @@ -1426,7 +1428,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.6. Shutdown smartly and gracefully -**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources +**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources **Otherwise:** Dying immediately means not responding to thousands of disappointed users From f799d88d26f8dd0d8414be15041eaf5380de7bdd Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 10 May 2023 18:37:10 +0300 Subject: [PATCH 1706/1795] Production practices --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index a64b2daf2..f9e92e6a4 100644 --- a/README.md +++ b/README.md @@ -271,6 +271,18 @@ my-system **TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry +```bash +my-system +├─ apps (components) + │ ├─ component-a +├─ libraries (generic cross-component functionality) +│ ├─ logger +│ │ ├─ package.json +│ │ ├─ src +│ │ │ ├─ index.js + +``` + **Otherwise:** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface 🔗 [**Read More: Structure by feature**](./sections/projectstructre/wraputilities.md) From c2af8ff9b28e312c9814495193eafe49694316eb Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 11 May 2023 10:54:49 +0300 Subject: [PATCH 1707/1795] Production practices --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f9e92e6a4..74a17e346 100644 --- a/README.md +++ b/README.md @@ -55,9 +55,9 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin 1. Project Architecture Practices (6) -  [1.1 Structure your solution by components `#strategic`](#-11-structure-your-solution-by-business-components)
    -  [1.2 Layer your components, keep the web layer within its boundaries `#strategic`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
    -  [1.3 Wrap common utilities as npm packages](#-13-wrap-common-utilities-as-npm-packages)
    +  [1.1 Structure your solution by components `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    +  [1.2 Layer your components, keep the web layer within its boundaries `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
    +  [1.3 Wrap common utilities as packages, consider publishing](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
      [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -70,15 +70,15 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [2.1 Use Async-Await or promises for async error handling](#-21-use-async-await-or-promises-for-async-error-handling)
    -  [2.2 Extend the built-in Error object `#strategic #updated`](#-22-extend-the-built-in-error-object)
    +  [2.2 Extend the built-in Error object `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    -  [2.5 Document API errors using OpenAPI or GraphQL `#updated`](#-25-document-api-errors-using-openapi-or -graphql)
    +  [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
    -  [2.8 Test error flows using your favorite test framework](#-28-test-error-flows-using-your-favorite-test-framework)
    +  [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
    -  [2.10 Catch unhandled promise rejections `#modified-recently`](#-210-catch-unhandled-promise-rejections)
    +  [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
      [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    @@ -90,14 +90,14 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [3.1 Use ESLint `#strategic`](#-31-use-eslint)
    -  [3.2 Node.js specific plugins](#-32-nodejs-specific-plugins)
    +  [3.2 Use Node.js eslint extension plugins `#updated`](#-32-use-nodejs-eslint-extension-plugins)
      [3.3 Start a Codeblock's Curly Braces on the Same Line](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
      [3.5 Name your functions](#-35-name-your-functions)
      [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
    -  [3.9 Require modules by folders, as opposed to the files directly](#-39-require-modules-by-folders-as-opposed-to-the-files-directly)
    +  [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 Use the === operator](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    @@ -122,7 +122,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
      [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
    -  [4.13 Test the five possible outcomes #strategic #new](#-413-test-the-five-possible-outcomes)
    +  [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    @@ -132,23 +132,23 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [5.1. Monitoring `#strategic`](#-51-monitoring)
    -  [5.2. Increase transparency using smart logging `#strategic`](#-52-increase-transparency-using-smart-logging)
    +  [5.2. Increase the observability using smart logging `#strategic`](#-52-increase-the-observability-using-smart-logging)
      [5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. Lock dependencies](#-54-lock-dependencies)
      [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
      [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    -  [5.8. Discover errors and downtime using APM products `#advanced`](#-58-discover-errors-and-downtime-using-apm-products)
    +  [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
      [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
      [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
      [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
    -  [5.12. Be stateless, kill your servers almost every day](#-512-be-stateless-kill-your-servers-almost-every-day)
    +  [5.12. Strive to be stateless `#strategic`](#-512-strive-to-be-stateless)
      [5.13. Use tools that automatically detect vulnerabilities](#-513-use-tools-that-automatically-detect-vulnerabilities)
      [5.14. Assign a transaction id to each log statement `#advanced`](#-514-assign-a-transaction-id-to-each-log-statement)
      [5.15. Set NODE_ENV=production](#-515-set-node_envproduction)
      [5.16. Design automated, atomic and zero-downtime deployments `#advanced`](#-516-design-automated-atomic-and-zero-downtime-deployments)
      [5.17. Use an LTS release of Node.js](#-517-use-an-lts-release-of-nodejs)
    -  [5.18. Don't route logs within the app](#-518-dont-route-logs-within-the-app)
    +  [5.18. Log to stdout, avoid specifying log destination within the app](#-518-log-to-stdout-avoid-specifying-log-destination-within-the-app)
      [5.19. Install your packages with npm ci `#new`](#-519-install-your-packages-with-npm-ci)
    @@ -209,7 +209,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [8.4. Use .dockerignore to prevent leaking secrets](#-84-use-dockerignore-to-prevent-leaking-secrets)
      [8.5. Clean-up dependencies before production](#-85-clean-up-dependencies-before-production)
      [8.6. Shutdown smartly and gracefully `#advanced`](#-86-shutdown-smartly-and-gracefully)
    -  [8.7. Set memory limits using both Docker and v8 `#advanced #strategic`](#-87-set-memory-limits-using-both-docker-and-v8)
    +  [8.7. Set memory limits using both Docker and v8 `#advanced` `#strategic`](#-87-set-memory-limits-using-both-docker-and-v8)
      [8.8. Plan for efficient caching](#-88-plan-for-efficient-caching)
      [8.9. Use explicit image reference, avoid latest tag](#-89-use-explicit-image-reference-avoid-latest-tag)
      [8.10. Prefer smaller Docker base images](#-810-prefer-smaller-docker-base-images)
    @@ -307,7 +307,7 @@ my-system ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully -**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features solely when absolutely necessary +**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises **Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time @@ -600,7 +600,7 @@ function doSomething() {

    -## ![✔] 3.9 Set an explicit entry point per module/folder +## ![✔] 3.9 Set an explicit entry point to a module/folder **TL;DR:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality From fb8ee242109295786415dbb27a22c8076654f485 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 11 May 2023 11:00:33 +0300 Subject: [PATCH 1708/1795] Production practices --- sections/projectstructre/choose-framework.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sections/projectstructre/choose-framework.md b/sections/projectstructre/choose-framework.md index 21b32ef0a..da03f6a69 100644 --- a/sections/projectstructre/choose-framework.md +++ b/sections/projectstructre/choose-framework.md @@ -1,4 +1,4 @@ -# Structure your solution by components +# Consider all the consequences when choosing the main framework

    @@ -37,8 +37,3 @@ Cons: Covers a small subset of a typical application needs - leaves a handful of **Prefer Fastify when -** The app consists of reasonably-sized components/Microservices (i.e., not a huge monolith); for teams who have solid JavaScript & Node.js knowledge; when sticking to Node.js narratives and spirit is desirable **Prefer Nest.js when** - It's desirable to design and code in OOP style; when the team is highly experienced with Java/Spring/Angular or similar; for large size app that can't be broken down (i.e. monolith) to autonomous component; for a team that lacks fundamental JavaScript/Node.js skills (not exclusively, this yet another consideration); when the decision-making overhead should be minimized; when the time to the first delivery is a critical factor - -

    - - -Get lost with express; Nest.js with Fastify, Fastify covers a lot, From 4f9757d212d4aae4c147d8690c28f124ee7f5181 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 11 May 2023 11:01:28 +0300 Subject: [PATCH 1709/1795] Production practices --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 74a17e346..f6c961e01 100644 --- a/README.md +++ b/README.md @@ -297,6 +297,8 @@ my-system 🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) +

    + ## ![✔] 1.5 Consider all the consequences when choosing the main framework **TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) From f3697d26ceec850980f7ab18c9a56f16112d2069 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 11 May 2023 11:02:20 +0300 Subject: [PATCH 1710/1795] Update choose-framework.md --- sections/projectstructre/choose-framework.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/sections/projectstructre/choose-framework.md b/sections/projectstructre/choose-framework.md index 21b32ef0a..da03f6a69 100644 --- a/sections/projectstructre/choose-framework.md +++ b/sections/projectstructre/choose-framework.md @@ -1,4 +1,4 @@ -# Structure your solution by components +# Consider all the consequences when choosing the main framework

    @@ -37,8 +37,3 @@ Cons: Covers a small subset of a typical application needs - leaves a handful of **Prefer Fastify when -** The app consists of reasonably-sized components/Microservices (i.e., not a huge monolith); for teams who have solid JavaScript & Node.js knowledge; when sticking to Node.js narratives and spirit is desirable **Prefer Nest.js when** - It's desirable to design and code in OOP style; when the team is highly experienced with Java/Spring/Angular or similar; for large size app that can't be broken down (i.e. monolith) to autonomous component; for a team that lacks fundamental JavaScript/Node.js skills (not exclusively, this yet another consideration); when the decision-making overhead should be minimized; when the time to the first delivery is a critical factor - -

    - - -Get lost with express; Nest.js with Fastify, Fastify covers a lot, From c8b7eb27aab77ca5c89aa329ebb2b49b249378bf Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 11 May 2023 11:52:27 +0300 Subject: [PATCH 1711/1795] Production practices --- README.md | 2 +- sections/projectstructre/choose-framework.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6c961e01..d811388ee 100644 --- a/README.md +++ b/README.md @@ -305,7 +305,7 @@ my-system **Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns -🔗 [**Read More: configuration best practices**](./sections/projectstructre/choose-framework.md) +🔗 [**Read More: Choosing the right framework**](./sections/projectstructre/choose-framework.md) ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully diff --git a/sections/projectstructre/choose-framework.md b/sections/projectstructre/choose-framework.md index da03f6a69..6af330a0f 100644 --- a/sections/projectstructre/choose-framework.md +++ b/sections/projectstructre/choose-framework.md @@ -26,7 +26,7 @@ Cons: Younger than others and not as popular yet; smaller eco-system compared to **Koa** -Pros When compared with express: it's Simpler and nimbler; modern API with async/await support; better performance +Pros: When compared with express: it's Simpler and nimbler; modern API with async/await support; better performance Cons: Covers a small subset of a typical application needs - leaves a handful of app concerns uncovered; Not as popular as express and Nest.js From ccc1f5e7102b1c0c0ace5a758503625e52fde151 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 12:37:09 +0300 Subject: [PATCH 1712/1795] add main hebrew readme page and translate intro --- README.hebrew.md | 1913 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1913 insertions(+) create mode 100644 README.hebrew.md diff --git a/README.hebrew.md b/README.hebrew.md new file mode 100644 index 000000000..9b0e927d8 --- /dev/null +++ b/README.hebrew.md @@ -0,0 +1,1913 @@ +[✔]: assets/images/checkbox-small-blue.png + +# שיטות עבודה מומלצות ב Node.js + +

    + Node.js Best Practices +

    + +
    + +
    + 102 items Last update: April 19, 2023 Updated for Node 14.0.0 +
    + +
    + +[](https://twitter.com/nodepractices/) **עיקבו אחרינו בטוויטר!** [**@nodepractices**](https://twitter.com/nodepractices/) + +
    + +לקריאה בשפות נוספות: [![CN](./assets/flags/CN.png)**סינית**](./README.chinese.md), [![FR](./assets/flags/FR.png)**צרפתית**](./README.french.md), [![BR](./assets/flags/BR.png)**פורטוגזית**](./README.brazilian-portuguese.md), [![RU](./assets/flags/RU.png)**רוסית**](./README.russian.md), [![PL](./assets/flags/PL.png)**פולנית**](./README.polish.md), [![JA](./assets/flags/JA.png)**יפנית**](./README.japanese.md), [![EU](./assets/flags/EU.png)**באסקית**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ספרדית**, ![HE](./assets/flags/HE.png)**עברית**, ![KR](./assets/flags/KR.png)**קוריאנית** ו ![TR](./assets/flags/TR.png)**טורקית** בתהליך! )](#translations) + +
    + +## 🚀 יש לנו [Node.js starter רשמי - Practica.js](https://github.com/practicajs/practica). השתמשו בזה כדי לייצר שלד חדש לפרוייקט שמבוס על כל שיטות העבודה המומלצות כלולות בפנים. או רק כדי ללמוד על ידי דוגמאות קוד. + +
    + +# Latest Best Practices and News + +- **🛰 2023 edition is released soon**: We're now writing the next edition, stay tuned? + +- **✨ 89,000 stars**: Blushing, surprised and proud! + +- **🔖 New menu and tags**: Our menu is collapsible now and includes `#tags`. New visitors can read `#strategic` items first. Returning visitors can focus on `#new` content. Seniors can filter for `#advanced` items. Courtesy of the one and only [Rubek Joshi](https://github.com/rubek-joshi) + +- **![FR](./assets/flags/FR.png) French translation!1! :** The latest translation that joins our international guide is French. Bienvenue + +

    + +# ברוכים הבאים! שלושה דברים שכדאי לדעת לפני שגוללים מטה + +**1. הנכם קוראים עשרות מאמרים של שיטות העבודה המומלצות ב Node.js -** המאגר הזה הוא סיכום לא יסולא בפז של שיטות העבודה המומלצות ב Node.js , כמו כן הוא נעשה על בשיתוף פעולה. + +**2. זהו האוסף הגדול ביותר, והוא ממשיך לגדול כל שבוע -** נכון לרגע זה, יש למעלה מ 100 שיטות עבודה מומלצות, המלצות ארכיטקטורה והמלצות סגנון כתיבה. נושאים חדשים ובקשות חדשות (PR's) מתווספים כל יום במטרה לשמור את התוכן מעודכן. אנחנו נשמח לראותכם תורמים לפה, בין אם לתקן שגיאות קוד, עזרה בתרגום, או להציע רעיונות מבריקים חדשים. ראו את [המדריך לכתיבת הנחיות](./.operations/writing-guidelines.md). + +**3. שיטות העבודה כוללות מידע נוסף -** רוב הנקודות כוללות קישור **🔗לקריאה נוספת** שמרחיב על ידי דוגמאות קוד, ציטוטים מבלוגים נבחרים ומידע נוסף. + +

    + +## תוכן העניינים + +
    + + 1. Project Architecture Practices (6) + + +  [1.1 Structure your solution by components `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    +  [1.2 Layer your components, keep the web layer within its boundaries `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
    +  [1.3 Wrap common utilities as packages, consider publishing](#-13-wrap-common-utilities-as-packages-consider-publishing)
    +  [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
    +  [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    +  [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    + +
    + +
    + + 2. Error Handling Practices (12) + + +  [2.1 Use Async-Await or promises for async error handling](#-21-use-async-await-or-promises-for-async-error-handling)
    +  [2.2 Extend the built-in Error object `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
    +  [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
    +  [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    +  [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
    +  [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
    +  [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
    +  [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
    +  [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
    +  [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
    +  [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
    +  [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    + +
    + +
    + + 3. Code Style Practices (12) + + +  [3.1 Use ESLint `#strategic`](#-31-use-eslint)
    +  [3.2 Use Node.js eslint extension plugins `#updated`](#-32-use-nodejs-eslint-extension-plugins)
    +  [3.3 Start a Codeblock's Curly Braces on the Same Line](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
    +  [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
    +  [3.5 Name your functions](#-35-name-your-functions)
    +  [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
    +  [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
    +  [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
    +  [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
    +  [3.10 Use the === operator](#-310-use-the--operator)
    +  [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
    +  [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    +  [3.13 Avoid effects outside of functions `#new`](#-313-avoid-effects-outside-of-functions)
    + +
    + +
    + + 4. Testing And Overall Quality Practices (13) + + +  [4.1 At the very least, write API (component) testing `#strategic`](#-41-at-the-very-least-write-api-component-testing)
    +  [4.2 Include 3 parts in each test name `#new`](#-42-include-3-parts-in-each-test-name)
    +  [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
    +  [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
    +  [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
    +  [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
    +  [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
    +  [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
    +  [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
    +  [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
    +  [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
    +  [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
    +  [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    + +
    + +
    + + 5. Going To Production Practices (19) + + +  [5.1. Monitoring `#strategic`](#-51-monitoring)
    +  [5.2. Increase the observability using smart logging `#strategic`](#-52-increase-the-observability-using-smart-logging)
    +  [5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
    +  [5.4. Lock dependencies](#-54-lock-dependencies)
    +  [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
    +  [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
    +  [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    +  [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
    +  [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
    +  [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
    +  [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
    +  [5.12. Strive to be stateless `#strategic`](#-512-strive-to-be-stateless)
    +  [5.13. Use tools that automatically detect vulnerabilities](#-513-use-tools-that-automatically-detect-vulnerabilities)
    +  [5.14. Assign a transaction id to each log statement `#advanced`](#-514-assign-a-transaction-id-to-each-log-statement)
    +  [5.15. Set NODE_ENV=production](#-515-set-node_envproduction)
    +  [5.16. Design automated, atomic and zero-downtime deployments `#advanced`](#-516-design-automated-atomic-and-zero-downtime-deployments)
    +  [5.17. Use an LTS release of Node.js](#-517-use-an-lts-release-of-nodejs)
    +  [5.18. Log to stdout, avoid specifying log destination within the app](#-518-log-to-stdout-avoid-specifying-log-destination-within-the-app)
    +  [5.19. Install your packages with npm ci `#new`](#-519-install-your-packages-with-npm-ci)
    + +
    + +
    + + 6. Security Practices (25) + + +  [6.1. Embrace linter security rules](#-61-embrace-linter-security-rules)
    +  [6.2. Limit concurrent requests using a middleware](#-62-limit-concurrent-requests-using-a-middleware)
    +  [6.3 Extract secrets from config files or use packages to encrypt them `#strategic`](#-63-extract-secrets-from-config-files-or-use-packages-to-encrypt-them)
    +  [6.4. Prevent query injection vulnerabilities with ORM/ODM libraries `#strategic`](#-64-prevent-query-injection-vulnerabilities-with-ormodm-libraries)
    +  [6.5. Collection of generic security best practices](#-65-collection-of-generic-security-best-practices)
    +  [6.6. Adjust the HTTP response headers for enhanced security](#-66-adjust-the-http-response-headers-for-enhanced-security)
    +  [6.7. Constantly and automatically inspect for vulnerable dependencies `#strategic`](#-67-constantly-and-automatically-inspect-for-vulnerable-dependencies)
    +  [6.8. Protect Users' Passwords/Secrets using bcrypt or scrypt `#strategic`](#-68-protect-users-passwordssecrets-using-bcrypt-or-scrypt)
    +  [6.9. Escape HTML, JS and CSS output](#-69-escape-html-js-and-css-output)
    +  [6.10. Validate incoming JSON schemas `#strategic`](#-610-validate-incoming-json-schemas)
    +  [6.11. Support blocklisting JWTs](#-611-support-blocklisting-jwts)
    +  [6.12. Prevent brute-force attacks against authorization `#advanced`](#-612-prevent-brute-force-attacks-against-authorization)
    +  [6.13. Run Node.js as non-root user](#-613-run-nodejs-as-non-root-user)
    +  [6.14. Limit payload size using a reverse-proxy or a middleware](#-614-limit-payload-size-using-a-reverse-proxy-or-a-middleware)
    +  [6.15. Avoid JavaScript eval statements](#-615-avoid-javascript-eval-statements)
    +  [6.16. Prevent evil RegEx from overloading your single thread execution](#-616-prevent-evil-regex-from-overloading-your-single-thread-execution)
    +  [6.17. Avoid module loading using a variable](#-617-avoid-module-loading-using-a-variable)
    +  [6.18. Run unsafe code in a sandbox](#-618-run-unsafe-code-in-a-sandbox)
    +  [6.19. Take extra care when working with child processes `#advanced`](#-619-take-extra-care-when-working-with-child-processes)
    +  [6.20. Hide error details from clients](#-620-hide-error-details-from-clients)
    +  [6.21. Configure 2FA for npm or Yarn `#strategic`](#-621-configure-2fa-for-npm-or-yarn)
    +  [6.22. Modify session middleware settings](#-622-modify-session-middleware-settings)
    +  [6.23. Avoid DOS attacks by explicitly setting when a process should crash `#advanced`](#-623-avoid-dos-attacks-by-explicitly-setting-when-a-process-should-crash)
    +  [6.24. Prevent unsafe redirects](#-624-prevent-unsafe-redirects)
    +  [6.25. Avoid publishing secrets to the npm registry](#-625-avoid-publishing-secrets-to-the-npm-registry)
    +  [6.26. 6.26 Inspect for outdated packages](#-626-inspect-for-outdated-packages)
    +  [6.27. Import built-in modules using the 'node:' protocol `#new`](#-627-import-built-in-modules-using-the-node-protocol)
    + +
    + +
    + + 7. Performance Practices (2) (Work In Progress️ ✍️) + + +  [7.1. Don't block the event loop](#-71-dont-block-the-event-loop)
    +  [7.2. Prefer native JS methods over user-land utils like Lodash](#-72-prefer-native-js-methods-over-user-land-utils-like-lodash)
    + +
    + +
    + + 8. Docker Practices (15) + + +  [8.1 Use multi-stage builds for leaner and more secure Docker images `#strategic`](#-81-use-multi-stage-builds-for-leaner-and-more-secure-docker-images)
    +  [8.2. Bootstrap using node command, avoid npm start](#-82-bootstrap-using-node-command-avoid-npm-start)
    +  [8.3. Let the Docker runtime handle replication and uptime `#strategic`](#-83-let-the-docker-runtime-handle-replication-and-uptime)
    +  [8.4. Use .dockerignore to prevent leaking secrets](#-84-use-dockerignore-to-prevent-leaking-secrets)
    +  [8.5. Clean-up dependencies before production](#-85-clean-up-dependencies-before-production)
    +  [8.6. Shutdown smartly and gracefully `#advanced`](#-86-shutdown-smartly-and-gracefully)
    +  [8.7. Set memory limits using both Docker and v8 `#advanced` `#strategic`](#-87-set-memory-limits-using-both-docker-and-v8)
    +  [8.8. Plan for efficient caching](#-88-plan-for-efficient-caching)
    +  [8.9. Use explicit image reference, avoid latest tag](#-89-use-explicit-image-reference-avoid-latest-tag)
    +  [8.10. Prefer smaller Docker base images](#-810-prefer-smaller-docker-base-images)
    +  [8.11. Clean-out build-time secrets, avoid secrets in args `#strategic #new`](#-811-clean-out-build-time-secrets-avoid-secrets-in-args)
    +  [8.12. Scan images for multi layers of vulnerabilities `#advanced`](#-812-scan-images-for-multi-layers-of-vulnerabilities)
    +  [8.13 Clean NODE_MODULE cache](#-813-clean-node_module-cache)
    +  [8.14. Generic Docker practices](#-814-generic-docker-practices)
    +  [8.15. Lint your Dockerfile `#new`](#-815-lint-your-dockerfile)
    + +
    + +

    + +# `1. Project Architecture Practices` + +## ![✔] 1.1 Structure your solution by business components + +**TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo + +```bash +my-system +├─ apps (components) +│ ├─ orders +│ ├─ users +│ ├─ payments +├─ libraries (generic cross-component functionality) +│ ├─ logger +│ ├─ authenticator +``` + +**Otherwise:** when artifacts from various modules/topics are mixed together, there are great chances of a tightly-coupled 'spaghetti' system. For example, in an architecture where 'module-a controller' might call 'module-b service', there are no clear modularity borders - every code change might affect anything else. With this approach, developers who code new features struggle to realize the scope and impact of their change. Consequently, they fear breaking other modules, and each deployment becomes slower and riskier + +🔗 [**Read More: structure by components**](./sections/projectstructre/breakintcomponents.md) + +

    + +## ![✔] 1.2 Layer your components with 3-tiers, keep the web layer within its boundaries + +**TL;DR:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal + +```bash +my-system +├─ apps (components) +│ ├─ component-a + │ ├─ entry-points + │ │ ├─ api # controller comes here + │ │ ├─ message-queue # message consumer comes here + │ ├─ domain # features and flows: DTO, services, logic + │ ├─ data-access # DB calls w/o ORM +``` + +**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc + +🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) + +

    + +## ![✔] 1.3 Wrap common utilities as packages, consider publishing + +**TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry + +```bash +my-system +├─ apps (components) + │ ├─ component-a +├─ libraries (generic cross-component functionality) +│ ├─ logger +│ │ ├─ package.json +│ │ ├─ src +│ │ │ ├─ index.js + +``` + +**Otherwise:** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface + +🔗 [**Read More: Structure by feature**](./sections/projectstructre/wraputilities.md) + +

    + +## ![✔] 1.4 Use environment aware, secure and hierarchical config + +**TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others + +**Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state + +🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) + +

    + +## ![✔] 1.5 Consider all the consequences when choosing the main framework + +**TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) + +**Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns + +🔗 [**Read More: Choosing the right framework**](./sections/projectstructre/choose-framework.md) + +## ![✔] 1.6 Use TypeScript sparingly and thoughtfully + +**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises + +**Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time + +🔗 [**Read More: TypeScript considerations**](./sections/projectstructre/typescript-considerations.md) + +


    + +

    ⬆ Return to top

    + +# `2. Error Handling Practices` + +## ![✔] 2.1 Use Async-Await or promises for async error handling + +**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch + +**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns + +🔗 [**Read More: avoiding callbacks**](./sections/errorhandling/asyncerrorhandling.md) + +

    + +## ![✔] 2.2 Extend the built-in Error object + +**TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) + +**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! + +🔗 [**Read More: using the built-in error object**](./sections/errorhandling/useonlythebuiltinerror.md) + +

    + +## ![✔] 2.3 Distinguish catastrophic errors from operational errors + +**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application + +**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context + +🔗 [**Read More: operational vs programmer error**](./sections/errorhandling/operationalvsprogrammererror.md) + +

    + +## ![✔] 2.4 Handle errors centrally, not within a middleware + +**TL;DR:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in + +**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors + +🔗 [**Read More: handling errors in a centralized place**](./sections/errorhandling/centralizedhandling.md) + +

    + +## ![✔] 2.5 Document API errors using OpenAPI or GraphQL + +**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well + +**Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) + +🔗 [**Read More: documenting API errors in Swagger or GraphQL**](./sections/errorhandling/documentingusingswagger.md) + +

    + +## ![✔] 2.6 Exit the process gracefully when a stranger comes to town + +**TL;DR:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart + +**Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily + +🔗 [**Read More: shutting the process**](./sections/errorhandling/shuttingtheprocess.md) + +

    + +## ![✔] 2.7 Use a mature logger to increase errors visibility + +**TL;DR:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator + +**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late + +🔗 [**Read More: using a mature logger**](./sections/errorhandling/usematurelogger.md) + +

    + +## ![✔] 2.8 Test error flows using your favorite test framework + +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) + +**Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling + +🔗 [**Read More: testing error flows**](./sections/errorhandling/testingerrorflows.md) + +

    + +## ![✔] 2.9 Discover errors and downtime using APM products + +**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: using APM products**](./sections/errorhandling/apmproducts.md) + +

    + +## ![✔] 2.10 Catch unhandled promise rejections + +**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` + +**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about + +🔗 [**Read More: catching unhandled promise rejection**](./sections/errorhandling/catchunhandledpromiserejection.md) + +

    + +## ![✔] 2.11 Fail fast, validate arguments using a dedicated library + +**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) + +**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? + +🔗 [**Read More: failing fast**](./sections/errorhandling/failfast.md) + +

    + +## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace + +**TL;DR:** Always do `return await` when returning a promise to benefit full error stacktrace. If a +function returns a promise, that function must be declared as `async` function and explicitly +`await` the promise before returning it + +**Otherwise:** The function that returns a promise without awaiting won't appear in the stacktrace. +Such missing frames would probably complicate the understanding of the flow that leads to the error, +especially if the cause of the abnormal behavior is inside of the missing function + +🔗 [**Read More: returning promises**](./sections/errorhandling/returningpromises.md) + +


    + +

    ⬆ Return to top

    + +# `3. Code Patterns And Style Practices` + +## ![✔] 3.1 Use ESLint + +**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint + +**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style + +🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) + +

    + +## ![✔] 3.2 Use Node.js eslint extension plugins + +**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules + +**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early + +

    + +## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line + +**TL;DR:** The opening curly braces of a code block should be on the same line as the opening statement + +### Code Example + +```javascript +// Do +function someFunction() { + // code block +} + +// Avoid +function someFunction() { + // code block +} +``` + +**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: + +🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) + +

    + +## ![✔] 3.4 Separate your statements properly + +No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. + +**TL;DR:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. + +**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. + +### Code example + +```javascript +// Do +function doThing() { + // ... +} + +doThing() + +// Do + +const items = [1, 2, 3] +items.forEach(console.log) + +// Avoid — throws exception +const m = new Map() +const a = [1,2,3] +[...m.values()].forEach(console.log) +> [...m.values()].forEach(console.log) +> ^^^ +> SyntaxError: Unexpected token ... + +// Avoid — throws exception +const count = 2 // it tries to run 2(), but 2 is not a function +(function doSomething() { + // do something amazing +}()) +// put a semicolon before the immediate invoked function, after the const definition, save the return value of the anonymous function to a variable or avoid IIFEs altogether +``` + +🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) +🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline) + +

    + +## ![✔] 3.5 Name your functions + +**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot + +**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions + +

    + +## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes + +**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short + +**Otherwise:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase + +### 3.6 Code Example + +```javascript +// for global variables names we use the const/let keyword and UPPER_SNAKE_CASE +let MUTABLE_GLOBAL = "mutable value"; +const GLOBAL_CONSTANT = "immutable value"; +const CONFIG = { + key: "value", +}; + +// examples of UPPER_SNAKE_CASE convention in nodejs/javascript ecosystem +// in javascript Math.PI module +const PI = 3.141592653589793; + +// https://github.com/nodejs/node/blob/b9f36062d7b5c5039498e98d2f2c180dca2a7065/lib/internal/http2/core.js#L303 +// in nodejs http2 module +const HTTP_STATUS_OK = 200; +const HTTP_STATUS_CREATED = 201; + +// for class name we use UpperCamelCase +class SomeClassExample { + // for static class properties we use UPPER_SNAKE_CASE + static STATIC_PROPERTY = "value"; +} + +// for functions names we use lowerCamelCase +function doSomething() { + // for scoped variable names we use the const/let keyword and lowerCamelCase + const someConstExample = "immutable value"; + let someMutableExample = "mutable value"; +} +``` + +

    + +## ![✔] 3.7 Prefer const over let. Ditch the var + +**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal + +**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes + +🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) + +

    + +## ![✔] 3.8 Require modules first, not inside functions + +**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems + +**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function + +

    + +## ![✔] 3.9 Set an explicit entry point to a module/folder + +**TL;DR:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality + +**Otherwise:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract + +### 3.9 Code example - avoid coupling the client to the module structure + +```javascript +// Avoid: client has deep familiarity with the internals + +// Client code +const SMSWithMedia = require("./SMSProvider/providers/media/media-provider.js"); + +// Better: explicitly export the public functions + +//index.js, module code +module.exports.SMSWithMedia = require("./SMSProvider/providers/media/media-provider.js"); + +// Client code +const { SMSWithMedia } = require("./SMSProvider"); +``` + +

    + +## ![✔] 3.10 Use the `===` operator + +**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal + +**Otherwise:** Unequal variables might return true when compared with the `==` operator + +### 3.10 Code example + +```javascript +"" == "0"; // false +0 == ""; // true +0 == "0"; // true + +false == "false"; // false +false == "0"; // true + +false == undefined; // false +false == null; // false +null == undefined; // true + +" \t\r\n " == 0; // true +``` + +All statements above will return false if used with `===` + +

    + +## ![✔] 3.11 Use Async Await, avoid callbacks + +**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch + +**Otherwise:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow + +🔗[**Read more:** Guide to async-await 1.0](https://github.com/yortus/asyncawait) + +

    + +## ![✔] 3.12 Use arrow function expressions (=>) + +**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) + +**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read + +🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) + +

    + +## ![✔] 3.13 Avoid effects outside of functions + +**TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns + +**Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data + +


    + +

    ⬆ Return to top

    + +# `4. Testing And Overall Quality Practices` + +\_We have dedicated guides for testing, see below. The best practices list here is a brief summary of these guides + +a. [JavaScript testing best practices](https://github.com/goldbergyoni/javascript-testing-best-practices) +b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +\_ + +## ![✔] 4.1 At the very least, write API (component) testing + +**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc + +**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage + +

    + +## ![✔] 4.2 Include 3 parts in each test name + +**TL;DR:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result + +**Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? + +🔗 [**Read More: Include 3 parts in each test name**](./sections/testingandquality/3-parts-in-name.md) + +

    + +## ![✔] 4.3 Structure tests by the AAA pattern + +**TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan + +**Otherwise:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain + +🔗 [**Read More: Structure tests by the AAA pattern**](./sections/testingandquality/aaa.md) + +

    + +## ![✔] 4.4 Ensure Node version is unified + +**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) + +**Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed + +

    + +## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test + +**TL;DR:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records + +**Otherwise:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build + +🔗 [**Read More: Avoid global test fixtures**](./sections/testingandquality/avoid-global-test-fixture.md) + +

    + +## ![✔] 4.6 Tag your tests + +**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' + +**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests + +

    + +## ![✔] 4.7 Check your test coverage, it helps to identify wrong test patterns + +**TL;DR:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold + +**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing + +

    + +## ![✔] 4.8 Use production-like environment for e2e testing + +**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) + +**Otherwise:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments + +

    + +## ![✔] 4.9 Refactor regularly using static analysis tools + +**TL;DR:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). + +**Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix + +🔗 [**Read More: Refactoring!**](./sections/testingandquality/refactoring.md) + +

    + +## ![✔] 4.10 Mock responses of external HTTP services + +**TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production + +**Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow + +🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) + +## ![✔] 4.11 Test your middlewares in isolation + +**TL;DR:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects + +**Otherwise:** A bug in Express middleware === a bug in all or most requests + +🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) + +## ![✔] 4.12 Specify a port in production, randomize in testing + +**TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port + +**Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default + +🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) + +## ![✔] 4.13 Test the five possible outcomes + +**TL;DR:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) + +**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) + +🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) + +


    + +

    ⬆ Return to top

    + +# `5. Going To Production Practices` + +## ![✔] 5.1. Monitoring + +**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions + +**Otherwise:** Failure === disappointed customers. Simple + +🔗 [**Read More: Monitoring!**](./sections/production/monitoring.md) + +

    + +## ![✔] 5.2. Increase the observability using smart logging + +**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted + +**Otherwise:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information + +🔗 [**Read More: Increase transparency using smart logging**](./sections/production/smartlogging.md) + +

    + +## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy + +**TL;DR:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead + +**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly + +🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](./sections/production/delegatetoproxy.md) + +

    + +## ![✔] 5.4. Lock dependencies + +**TL;DR:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical + +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code + +🔗 [**Read More: Lock dependencies**](./sections/production/lockdependencies.md) + +

    + +## ![✔] 5.5. Guard process uptime using the right tool + +**TL;DR:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location + +**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos + +🔗 [**Read More: Guard process uptime using the right tool**](./sections/production/guardprocess.md) + +

    + +## ![✔] 5.6. Utilize all CPU cores + +**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) + +**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) + +🔗 [**Read More: Utilize all CPU cores**](./sections/production/utilizecpu.md) + +

    + +## ![✔] 5.7. Create a ‘maintenance endpoint’ + +**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code + +**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes + +🔗 [**Read More: Create a ‘maintenance endpoint’**](./sections/production/createmaintenanceendpoint.md) + +

    + +## ![✔] 5.8. Discover the unknowns using APM products + +**TL;DR:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example + +**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX + +🔗 [**Read More: Discover errors and downtime using APM products**](./sections/production/apmproducts.md) + +

    + +## ![✔] 5.9. Make your code production-ready + +**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') + +**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written + +🔗 [**Read More: Make your code production-ready**](./sections/production/productioncode.md) + +

    + +## ![✔] 5.10. Measure and guard the memory usage + +**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system + +**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) + +🔗 [**Read More: Measure and guard the memory usage**](./sections/production/measurememory.md) + +

    + +## ![✔] 5.11. Get your frontend assets out of Node + +**TL;DR:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering + +**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content + +🔗 [**Read More: Get your frontend assets out of Node**](./sections/production/frontendout.md) + +

    + +## ![✔] 5.12. Strive to be stateless + +**TL;DR:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically + +**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server + +🔗 [**Read More: Be stateless, kill your Servers almost every day**](./sections/production/bestateless.md) + +

    + +## ![✔] 5.13. Use tools that automatically detect vulnerabilities + +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately + +**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious + +🔗 [**Read More: Use tools that automatically detect vulnerabilities**](./sections/production/detectvulnerabilities.md) + +

    + +## ![✔] 5.14. Assign a transaction id to each log statement + +**TL;DR:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside + +**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue + +🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](./sections/production/assigntransactionid.md) + +

    + +## ![✔] 5.15. Set `NODE_ENV=production` + +**TL;DR:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production + +**Otherwise:** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering + +🔗 [**Read More: Set NODE_ENV=production**](./sections/production/setnodeenv.md) + +

    + +## ![✔] 5.16. Design automated, atomic and zero-downtime deployments + +**TL;DR:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment + +**Otherwise:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features + +

    + +## ![✔] 5.17. Use an LTS release of Node.js + +**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements + +**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain + +🔗 [**Read More: Use an LTS release of Node.js**](./sections/production/LTSrelease.md) + +

    + +## ![✔] 5.18. Log to stdout, avoid specifying log destination within the app + +**TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). + +**Otherwise:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive + +🔗 [**Read More: Log Routing**](./sections/production/logrouting.md) + +

    + +## ![✔] 5.19. Install your packages with `npm ci` + +**TL;DR:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files + +**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. + +🔗 [**Read More: Use npm ci**](./sections/production/installpackageswithnpmci.md) + +


    + +

    ⬆ Return to top

    + +# `6. Security Best Practices` + +
    +54 items +
    + +## ![✔] 6.1. Embrace linter security rules + + + +**TL;DR:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter + +**Otherwise:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories + +🔗 [**Read More: Lint rules**](./sections/security/lintrules.md) + +

    + +## ![✔] 6.2. Limit concurrent requests using a middleware + + + +**TL;DR:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) + +**Otherwise:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. + +🔗 [**Read More: Implement rate limiting**](./sections/security/limitrequests.md) + +

    + +## ![✔] 6.3 Extract secrets from config files or use packages to encrypt them + + + +**TL;DR:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally + +**Otherwise:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). + +🔗 [**Read More: Secret management**](./sections/security/secretmanagement.md) + +

    + +## ![✔] 6.4. Prevent query injection vulnerabilities with ORM/ODM libraries + + + +**TL;DR:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. + +**Otherwise:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. + +🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](./sections/security/ormodmusage.md) + +

    + +## ![✔] 6.5. Collection of generic security best practices + +**TL;DR:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Common security best practices**](./sections/security/commonsecuritybestpractices.md) + +

    + +## ![✔] 6.6. Adjust the HTTP response headers for enhanced security + + + +**TL;DR:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). + +**Otherwise:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities + +🔗 [**Read More: Using secure headers in your application**](./sections/security/secureheaders.md) + +

    + +## ![✔] 6.7. Constantly and automatically inspect for vulnerable dependencies + + + +**TL;DR:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. + +**Otherwise:** An attacker could detect your web framework and attack all its known vulnerabilities. + +🔗 [**Read More: Dependency security**](./sections/security/dependencysecurity.md) + +

    + +## ![✔] 6.8. Protect Users' Passwords/Secrets using bcrypt or scrypt + + + +**TL;DR:** Passwords or secrets (e.g. API keys) should be stored using a secure hash + salt function like `bcrypt`,`scrypt`, or worst case `pbkdf2`. + +**Otherwise:** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. + +🔗 [**Read More: User Passwords**](./sections/security/userpasswords.md) + +

    + +## ![✔] 6.9. Escape HTML, JS and CSS output + + + +**TL;DR:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) + +**Otherwise:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients + +🔗 [**Read More: Escape output**](./sections/security/escape-output.md) + +

    + +## ![✔] 6.10. Validate incoming JSON schemas + + + +**TL;DR:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) + +**Otherwise:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application + +🔗 [**Read More: Validate incoming JSON schemas**](./sections/security/validation.md) + +

    + +## ![✔] 6.11. Support blocklisting JWTs + + + +**TL;DR:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blocklist of untrusted tokens that are validated on each request. + +**Otherwise:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. + +🔗 [**Read More: Blocklist JSON Web Tokens**](./sections/security/expirejwt.md) + +

    + +## ![✔] 6.12. Prevent brute-force attacks against authorization + + + +**TL;DR:** A simple and powerful technique is to limit authorization attempts using two metrics: + +1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. +2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. + +**Otherwise:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application + +🔗 [**Read More: Login rate limiting**](./sections/security/login-rate-limit.md) + +

    + +## ![✔] 6.13. Run Node.js as non-root user + + + +**TL;DR:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" + +**Otherwise:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) + +🔗 [**Read More: Run Node.js as non-root user**](./sections/security/non-root-user.md) + +

    + +## ![✔] 6.14. Limit payload size using a reverse-proxy or a middleware + + + +**TL;DR:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads + +**Otherwise:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks + +🔗 [**Read More: Limit payload size**](./sections/security/requestpayloadsizelimit.md) + +

    + +## ![✔] 6.15. Avoid JavaScript eval statements + + + +**TL;DR:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. + +**Otherwise:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. + +🔗 [**Read More: Avoid JavaScript eval statements**](./sections/security/avoideval.md) + +

    + +## ![✔] 6.16. Prevent evil RegEx from overloading your single thread execution + + + +**TL;DR:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns + +**Otherwise:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 + +🔗 [**Read More: Prevent malicious RegEx**](./sections/security/regex.md) + +

    + +## ![✔] 6.17. Avoid module loading using a variable + + + +**TL;DR:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough + +**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. + +🔗 [**Read More: Safe module loading**](./sections/security/safemoduleloading.md) + +

    + +## ![✔] 6.18. Run unsafe code in a sandbox + + + +**TL;DR:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox + +**Otherwise:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables + +🔗 [**Read More: Run unsafe code in a sandbox**](./sections/security/sandbox.md) + +

    + +## ![✔] 6.19. Take extra care when working with child processes + + + +**TL;DR:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. + +**Otherwise:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. + +🔗 [**Read More: Be cautious when working with child processes**](./sections/security/childprocesses.md) + +

    + +## ![✔] 6.20. Hide error details from clients + + + +**TL;DR:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details + +**Otherwise:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace + +🔗 [**Read More: Hide error details from client**](./sections/security/hideerrors.md) + +

    + +## ![✔] 6.21. Configure 2FA for npm or Yarn + + + +**TL;DR:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. + +**Otherwise:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) + +

    + +## ![✔] 6.22. Modify session middleware settings + + + +**TL;DR:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) + +**Otherwise:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities + +🔗 [**Read More: Cookie and session security**](./sections/security/sessions.md) + +

    + +## ![✔] 6.23. Avoid DOS attacks by explicitly setting when a process should crash + + + +**TL;DR:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) + +**Otherwise:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease + +

    + +## ![✔] 6.24. Prevent unsafe redirects + + + +**TL;DR:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. + +**Otherwise:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. + +🔗 [**Read More: Prevent unsafe redirects**](./sections/security/saferedirects.md) + +

    + +## ![✔] 6.25. Avoid publishing secrets to the npm registry + + + +**TL;DR:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to ignore specific files or folders, or the `files` array in `package.json` can act as an allow list. + +**Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. + +🔗 [**Read More: Avoid publishing secrets**](./sections/security/avoid_publishing_secrets.md) + +

    + +## ![✔] 6.26 Inspect for outdated packages + +**TL;DR:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version + +**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky + +

    + +## ![✔] 6.27. Import built-in modules using the 'node:' protocol + + + +**TL;DR:** Import or require built-in Node.js modules using the 'node protocol' syntax: + +```javascript +import { functionName } from "node:module"; // note that 'node:' prefix +``` + +For example: + +```javascript +import { createServer } from "node:http"; +``` + +This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to a well-trusted official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) + +**Otherwise:** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them + +


    + +

    ⬆ Return to top

    + +# `7. Draft: Performance Best Practices` + +## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/256) + +

    + +## ![✔] 7.1. Don't block the event loop + +**TL;DR:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. + +**Otherwise:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** + +🔗 [**Read More: Do not block the event loop**](./sections/performance/block-loop.md) + +


    + +## ![✔] 7.2. Prefer native JS methods over user-land utils like Lodash + +**TL;DR:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. +Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. + +**Otherwise:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. + +🔗 [**Read More: Native over user land utils**](./sections/performance/nativeoverutil.md) + +


    + +

    ⬆ Return to top

    + +# `8. Docker Best Practices` + +🏅 Many thanks to [Bret Fisher](https://github.com/BretFisher) from whom we learned many of the following practices + +

    + +## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images + +**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. + +**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. + +### Example Dockerfile for multi-stage builds + +```dockerfile +FROM node:14.4.0 AS build + +COPY . . +RUN npm ci && npm run build + + +FROM node:slim-14.4.0 + +USER node +EXPOSE 8080 + +COPY --from=build /home/node/app/dist /home/node/app/package.json /home/node/app/package-lock.json ./ +RUN npm ci --production + +CMD [ "node", "dist/app.js" ] +``` + +🔗 [**Read More: Use multi-stage builds**](./sections/docker/multi_stage_builds.md) + +


    + +## ![✔] 8.2. Bootstrap using `node` command, avoid `npm start` + +**TL;DR:** Use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-processes, signal handling, graceful shutdown and having zombie processes + +Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm/changelog#706-2020-10-27) to pass signals. We follow and will update accordingly + +**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data + +[**Read More: Bootstrap container using node command, avoid npm start**](./sections/docker/bootstrap-using-node.md) + +


    + +## ![✔] 8.3. Let the Docker runtime handle replication and uptime + +**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes + +**Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance + +🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](./sections/docker/restart-and-replicate-processes.md) + +


    + +## ![✔] 8.4. Use .dockerignore to prevent leaking secrets + +**TL;DR**: Include a `.dockerignore` file that filters out common secret files and development artifacts. By doing so, you might prevent secrets from leaking into the image. As a bonus the build time will significantly decrease. Also, ensure not to copy all files recursively rather explicitly choose what should be copied to Docker + +**Otherwise**: Common personal secret files like `.env`, `.aws` and `.npmrc` will be shared with anybody with access to the image (e.g. Docker repository) + +🔗 [**Read More: Use .dockerignore**](./sections/docker/docker-ignore.md) + +


    + +## ![✔] 8.5. Clean-up dependencies before production + +**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` + +**Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) + +🔗 Read More: [Remove development dependencies](./sections/docker/install-for-production.md) + +


    + +## ![✔] 8.6. Shutdown smartly and gracefully + +**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources + +**Otherwise:** Dying immediately means not responding to thousands of disappointed users + +🔗 [**Read More: Graceful shutdown**](./sections/docker/graceful-shutdown.md) + +


    + +## ![✔] 8.7. Set memory limits using both Docker and v8 + +**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit + +**Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources + +🔗 [**Read More: Set memory limits using Docker only**](./sections/docker/memory-limit.md) + +


    + +## ![✔] 8.8. Plan for efficient caching + +**TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. + +**Otherwise:** Docker build will be very long and consume lot of resources even when making tiny changes + +🔗 [**Read More: Leverage caching to reduce build times**](./sections/docker/use-cache-for-shorter-build-time.md) + +


    + +## ![✔] 8.9. Use explicit image reference, avoid `latest` tag + +**TL;DR:** Specify an explicit image digest or versioned label, never refer to `latest`. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. + +In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. + +**Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. + +🔗 [**Read More: Understand image tags and use the "latest" tag with caution**](./sections/docker/image-tags.md) + +


    + +## ![✔] 8.10. Prefer smaller Docker base images + +**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. + +**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. + +🔗 [**Read More: Prefer smaller images**](./sections/docker/smaller_base_images.md) + +


    + +## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args + +**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces + +**Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus + +🔗 [**Read More: Clean-out build-time secrets**](./sections/docker/avoid-build-time-secrets.md) + +


    + +## ![✔] 8.12. Scan images for multi layers of vulnerabilities + +**TL;DR:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins + +**Otherwise:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications + +🔗 [**Read More: Scan the entire image before production**](./sections/docker/scan-images.md) + +


    + +## ![✔] 8.13 Clean NODE_MODULE cache + +**TL;DR:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off + +**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used + +🔗 [**Read More: Clean NODE_MODULE cache**](./sections/docker/clean-cache.md) + +


    + +## ![✔] 8.14. Generic Docker practices + +**TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. + +🔗 [**Read More: Generic Docker practices**](./sections/docker/generic-tips.md) + +


    + +## ![✔] 8.15. Lint your Dockerfile + +**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. + +**Otherwise:** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. + +🔗 [**Read More: Lint your Dockerfile**](./sections/docker/lint-dockerfile.md) + +


    + +

    ⬆ Return to top

    + +# Milestones + +To maintain this guide and keep it up to date, we are constantly updating and improving the guidelines and best practices with the help of the community. You can follow our [milestones](https://github.com/goldbergyoni/nodebestpractices/milestones) and join the working groups if you want to contribute to this project + +
    + +## Translations + +All translations are contributed by the community. We will be happy to get any help with either completed, ongoing or new translations! + +### Completed translations + +- ![BR](./assets/flags/BR.png) [Brazilian Portuguese](./README.brazilian-portuguese.md) - Courtesy of [Marcelo Melo](https://github.com/marcelosdm) +- ![CN](./assets/flags/CN.png) [Chinese](./README.chinese.md) - Courtesy of [Matt Jin](https://github.com/mattjin) +- ![RU](./assets/flags/RU.png) [Russian](./README.russian.md) - Courtesy of [Alex Ivanov](https://github.com/contributorpw) +- ![PL](./assets/flags/PL.png) [Polish](./README.polish.md) - Courtesy of [Michal Biesiada](https://github.com/mbiesiad) +- ![JA](./assets/flags/JA.png) [Japanese](./README.japanese.md) - Courtesy of [Yuki Ota](https://github.com/YukiOta), [Yuta Azumi](https://github.com/YA21) +- ![EU](./assets/flags/EU.png) [Basque](README.basque.md) - Courtesy of [Ane Diaz de Tuesta](https://github.com/anediaz) & Joxefe Diaz de Tuesta + +### Translations in progress + +- ![FR](./assets/flags/FR.png) [French](./README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) +- ![HE](./assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![KR](./assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) +- ![ES](./assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) +- ![TR](./assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139)) + +

    + +## Steering Committee + +Meet the steering committee members - the people who work together to provide guidance and future direction to the project. In addition, each member of the committee leads a project tracked under our [GitHub projects](https://github.com/goldbergyoni/nodebestpractices/projects). + + + +[Yoni Goldberg](https://github.com/goldbergyoni) + + + +Independent Node.js consultant who works with customers in the USA, Europe, and Israel on building large-scale Node.js applications. Many of the best practices above were first published at [goldbergyoni.com](https://goldbergyoni.com). Reach Yoni at [@goldbergyoni](https://github.com/goldbergyoni) or [me@goldbergyoni.com](mailto:me@goldbergyoni.com) + +
    + +Josh Hemphill + +[Josh Hemphill](https://github.com/josh-hemphill) + + + + +Full Stack Software Engineer / Developer specializing in Security, DevOps/DevSecOps, and ERP Integrations. + +
    + +Raz Luvaton + +[Raz Luvaton](https://github.com/rluvaton) + + + +Full Stack Developer who knows how to exit from Vim and loves Architecture, Virtualization and Security. + +
    + +## Contributing + +If you've ever wanted to contribute to open source, now is your chance! See the [contributing docs](.operations/CONTRIBUTING.md) for more information. + +## Contributors ✨ + +Thanks goes to these wonderful people who have contributed to this repository! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Kevin Rambaud
    Kevin Rambaud

    🖋
    Michael Fine
    Michael Fine

    🖋
    Shreya Dahal
    Shreya Dahal

    🖋
    Matheus Cruz Rocha
    Matheus Cruz Rocha

    🖋
    Yog Mehta
    Yog Mehta

    🖋
    Kudakwashe Paradzayi
    Kudakwashe Paradzayi

    🖋
    t1st3
    t1st3

    🖋
    mulijordan1976
    mulijordan1976

    🖋
    Matan Kushner
    Matan Kushner

    🖋
    Fabio Hiroki
    Fabio Hiroki

    🖋
    James Sumners
    James Sumners

    🖋
    Dan Gamble
    Dan Gamble

    🖋
    PJ Trainor
    PJ Trainor

    🖋
    Remek Ambroziak
    Remek Ambroziak

    🖋
    Yoni Jah
    Yoni Jah

    🖋
    Misha Khokhlov
    Misha Khokhlov

    🖋
    Evgeny Orekhov
    Evgeny Orekhov

    🖋
    -
    -

    🖋
    Isaac Halvorson
    Isaac Halvorson

    🖋
    Vedran Karačić
    Vedran Karačić

    🖋
    lallenlowe
    lallenlowe

    🖋
    Nathan Wells
    Nathan Wells

    🖋
    Paulo Reis
    Paulo Reis

    🖋
    syzer
    syzer

    🖋
    David Sancho
    David Sancho

    🖋
    Robert Manolea
    Robert Manolea

    🖋
    Xavier Ho
    Xavier Ho

    🖋
    Aaron
    Aaron

    🖋
    Jan Charles Maghirang Adona
    Jan Charles Maghirang Adona

    🖋
    Allen
    Allen

    🖋
    Leonardo Villela
    Leonardo Villela

    🖋
    Michał Załęcki
    Michał Załęcki

    🖋
    Chris Nicola
    Chris Nicola

    🖋
    Alejandro Corredor
    Alejandro Corredor

    🖋
    cwar
    cwar

    🖋
    Yuwei
    Yuwei

    🖋
    Utkarsh Bhatt
    Utkarsh Bhatt

    🖋
    Duarte Mendes
    Duarte Mendes

    🖋
    Jason Kim
    Jason Kim

    🖋
    Mitja O.
    Mitja O.

    🖋
    Sandro Miguel Marques
    Sandro Miguel Marques

    🖋
    Gabe
    Gabe

    🖋
    Ron Gross
    Ron Gross

    🖋
    Valeri Karpov
    Valeri Karpov

    🖋
    Sergio Bernal
    Sergio Bernal

    🖋
    Nikola Telkedzhiev
    Nikola Telkedzhiev

    🖋
    Vitor Godoy
    Vitor Godoy

    🖋
    Manish Saraan
    Manish Saraan

    🖋
    Sangbeom Han
    Sangbeom Han

    🖋
    blackmatch
    blackmatch

    🖋
    Joe Reeve
    Joe Reeve

    🖋
    Ryan Busby
    Ryan Busby

    🖋
    Iman Mohamadi
    Iman Mohamadi

    🖋
    Sergii Paryzhskyi
    Sergii Paryzhskyi

    🖋
    Kapil Patel
    Kapil Patel

    🖋
    迷渡
    迷渡

    🖋
    Hozefa
    Hozefa

    🖋
    Ethan
    Ethan

    🖋
    Sam
    Sam

    🖋
    Arlind
    Arlind

    🖋
    Teddy Toussaint
    Teddy Toussaint

    🖋
    Lewis
    Lewis

    🖋
    Gabriel Lidenor
    Gabriel Lidenor

    🖋
    Roman
    Roman

    🖋
    Francozeira
    Francozeira

    🖋
    Invvard
    Invvard

    🖋
    Rômulo Garofalo
    Rômulo Garofalo

    🖋
    Tho Q Luong
    Tho Q Luong

    🖋
    Burak Shen
    Burak Shen

    🖋
    Martin Muzatko
    Martin Muzatko

    🖋
    Jared Collier
    Jared Collier

    🖋
    Hilton Meyer
    Hilton Meyer

    🖋
    ChangJoo Park(박창주)
    ChangJoo Park(박창주)

    🖋
    Masahiro Sakaguchi
    Masahiro Sakaguchi

    🖋
    Keith Holliday
    Keith Holliday

    🖋
    coreyc
    coreyc

    🖋
    Maximilian Berkmann
    Maximilian Berkmann

    🖋
    Douglas Mariano Valero
    Douglas Mariano Valero

    🖋
    Marcelo Melo
    Marcelo Melo

    🖋
    Mehmet Perk
    Mehmet Perk

    🖋
    ryan ouyang
    ryan ouyang

    🖋
    Shabeer
    Shabeer

    🖋
    Eduard Kyvenko
    Eduard Kyvenko

    🖋
    Deyvison Rocha
    Deyvison Rocha

    🖋
    George Mamer
    George Mamer

    🖋
    Konstantinos Leimonis
    Konstantinos Leimonis

    🖋
    Oliver Lluberes
    Oliver Lluberes

    🌍
    Tien Do
    Tien Do

    🖋
    Ranvir Singh
    Ranvir Singh

    🖋
    Vadim Nicolaev
    Vadim Nicolaev

    🖋 🌍
    German Gamboa Gonzalez
    German Gamboa Gonzalez

    🖋
    Hafez
    Hafez

    🖋
    Chandiran
    Chandiran

    🖋
    VinayaSathyanarayana
    VinayaSathyanarayana

    🖋
    Kim Kern
    Kim Kern

    🖋
    Kenneth Freitas
    Kenneth Freitas

    🖋
    songe
    songe

    🖋
    Kirill Shekhovtsov
    Kirill Shekhovtsov

    🖋
    Serge
    Serge

    🖋
    keyrwinz
    keyrwinz

    🖋
    Dmitry Nikitenko
    Dmitry Nikitenko

    🖋
    bushuai
    bushuai

    👀 🖋
    Benjamin Gruenbaum
    Benjamin Gruenbaum

    🖋
    Ezequiel
    Ezequiel

    🌍
    Juan José Rodríguez
    Juan José Rodríguez

    🌍
    Or Bin
    Or Bin

    🖋
    Andreo Vieira
    Andreo Vieira

    🖋
    Michael Solomon
    Michael Solomon

    🖋
    Jimmy Callin
    Jimmy Callin

    🖋
    Siddharth
    Siddharth

    🖋
    Ryan Smith
    Ryan Smith

    🖋
    Tom Boettger
    Tom Boettger

    🖋
    Joaquín Ormaechea
    Joaquín Ormaechea

    🌍
    dfrzuz
    dfrzuz

    🌍
    Victor Homyakov
    Victor Homyakov

    🖋
    Josh
    Josh

    🖋 🛡️
    Alec Francis
    Alec Francis

    🖋
    arjun6610
    arjun6610

    🖋
    Jan Osch
    Jan Osch

    🖋
    Thiago Rotondo Sampaio
    Thiago Rotondo Sampaio

    🌍
    Alexsey
    Alexsey

    🖋
    Luis A. Acurero
    Luis A. Acurero

    🌍
    Lucas Romano
    Lucas Romano

    🌍
    Denise Case
    Denise Case

    🖋
    Nick Ribal
    Nick Ribal

    🖋 👀
    0xflotus
    0xflotus

    🖋
    Jonathan Chen
    Jonathan Chen

    🖋
    Dilan Srilal
    Dilan Srilal

    🖋
    vladthelittleone
    vladthelittleone

    🌍
    Nik Osvalds
    Nik Osvalds

    🖋
    Daniel Kiss
    Daniel Kiss

    📖
    Forresst
    Forresst

    🖋
    Jonathan Svenheden
    Jonathan Svenheden

    🖋
    AustrisC
    AustrisC

    🖋
    kyeongtae kim
    kyeongtae kim

    🌍
    007
    007

    🖋
    Ane Diaz de Tuesta
    Ane Diaz de Tuesta

    🌍 🖋
    YukiOta
    YukiOta

    🌍
    Frazer Smith
    Frazer Smith

    🖋
    Raz Luvaton
    Raz Luvaton

    🖋
    Yuta Azumi
    Yuta Azumi

    🖋
    andrewjbarbour
    andrewjbarbour

    🖋
    mr
    mr

    🖋
    Aleksandar
    Aleksandar

    🖋
    Owl
    Owl

    🖋
    Yedidya Schwartz
    Yedidya Schwartz

    🖋 💡
    ari
    ari

    🖋
    Thomas König
    Thomas König

    🖋
    Kalle Lämsä
    Kalle Lämsä

    🖋
    Wyatt
    Wyatt

    🖋
    KHADIR Tayeb
    KHADIR Tayeb

    🖋
    Shankar Regmi
    Shankar Regmi

    🖋
    Shubham
    Shubham

    🖋
    Lucas Alves
    Lucas Alves

    🖋
    Benjamin
    Benjamin

    🖋
    Yeoh Joer
    Yeoh Joer

    🖋
    Miigon
    Miigon

    🖋
    Rostislav Bogorad
    Rostislav Bogorad

    🖋
    Flouse
    Flouse

    🖋
    Tarantini Pereira
    Tarantini Pereira

    🖋
    Kazuki Matsuo
    Kazuki Matsuo

    🖋
    Adam Smith
    Adam Smith

    🖋
    Dohyeon Ko
    Dohyeon Ko

    🖋
    Vladislav Legkov
    Vladislav Legkov

    🖋
    Kerollos Magdy
    Kerollos Magdy

    🖋
    Erez Lieberman
    Erez Lieberman

    🖋
    Breno Macedo
    Breno Macedo

    🖋
    Fernando Flores
    Fernando Flores

    🌍
    Rafael Brito
    Rafael Brito

    🌍
    Emiliano Peralta
    Emiliano Peralta

    🌍
    Shin, SJ
    Shin, SJ

    🖋
    Benjamin Forster
    Benjamin Forster

    🖋
    Daniele Fedeli
    Daniele Fedeli

    🖋
    djob195
    djob195

    🖋
    antspk
    antspk

    🖋
    정진영
    정진영

    🖋
    kkk-cashwalk
    kkk-cashwalk

    🖋
    apainintheneck
    apainintheneck

    🖋
    Fajar Budhi Iswanda
    Fajar Budhi Iswanda

    🖋
    이주호
    이주호

    🖋
    Singh
    Singh

    🖋
    Alex Dumitru
    Alex Dumitru

    🖋
    Anton Lykhatskyi
    Anton Lykhatskyi

    🖋
    sangwonlee
    sangwonlee

    🖋
    Eugenio Berretta
    Eugenio Berretta

    🖋
    soranakk
    soranakk

    🖋
    고준영
    고준영

    🖋 💻
    Guilherme Portella
    Guilherme Portella

    🖋
    André Esser
    André Esser

    🖋
    Scc
    Scc

    🌍
    Mauro Accornero
    Mauro Accornero

    🖋
    no-yan
    no-yan

    🖋
    + + + + + + +### Steering Committee Emeriti + +[Bruno Scheufler](https://github.com/BrunoScheufler) + + +💻 full-stack web engineer, Node.js & GraphQL enthusiast + +
    + + + +[Kyle Martin](https://github.com/js-kyle) + + + +Full Stack Developer & Site Reliability Engineer based in New Zealand, interested in web application security, and architecting and building Node.js applications to perform at global scale. + +
    + + + +[Kevyn Bruyere](https://github.com/kevynb) + + +Independent full-stack developer with a taste for Ops and automation. + +
    + + + +[Sagir Khan](https://github.com/sagirk) + + + + +Deep specialist in JavaScript and its ecosystem — React, Node.js, TypeScript, GraphQL, MongoDB, pretty much anything that involves JS/JSON in any layer of the system — building products using the web platform for the world’s most recognized brands. Individual Member of the Node.js Foundation. From 207f304c79d43f102a8864122714be325eb59df8 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 12:39:42 +0300 Subject: [PATCH 1713/1795] fix typo --- README.hebrew.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.hebrew.md b/README.hebrew.md index 9b0e927d8..eb7f7ff2c 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -22,7 +22,7 @@
    -## 🚀 יש לנו [Node.js starter רשמי - Practica.js](https://github.com/practicajs/practica). השתמשו בזה כדי לייצר שלד חדש לפרוייקט שמבוס על כל שיטות העבודה המומלצות כלולות בפנים. או רק כדי ללמוד על ידי דוגמאות קוד. +## 🚀 יש לנו [Node.js starter רשמי - Practica.js](https://github.com/practicajs/practica). השתמשו בזה כדי לייצר שלד חדש לפרוייקט שמבוסס על כל שיטות העבודה המומלצות כלולות בפנים. או רק כדי ללמוד על ידי דוגמאות קוד.
    From 8770e20b723fdff38368bf202f256128bf94c3c8 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 14:17:30 +0300 Subject: [PATCH 1714/1795] translate main titles --- README.hebrew.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index eb7f7ff2c..5bb1e68d1 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -52,7 +52,7 @@
    - 1. Project Architecture Practices (6) + 1. מבנה הפרוייקט (6)   [1.1 Structure your solution by components `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    @@ -66,7 +66,7 @@
    - 2. Error Handling Practices (12) + 2. ניהול שגיאות (12)   [2.1 Use Async-Await or promises for async error handling](#-21-use-async-await-or-promises-for-async-error-handling)
    @@ -86,7 +86,7 @@
    - 3. Code Style Practices (12) + 3. תבניות קוד וסגנון עיצוב (12)   [3.1 Use ESLint `#strategic`](#-31-use-eslint)
    @@ -107,7 +107,7 @@
    - 4. Testing And Overall Quality Practices (13) + 4. בדיקות ובקרת איכות (13)   [4.1 At the very least, write API (component) testing `#strategic`](#-41-at-the-very-least-write-api-component-testing)
    @@ -128,7 +128,7 @@
    - 5. Going To Production Practices (19) + 5. עלייה לאוויר (19)   [5.1. Monitoring `#strategic`](#-51-monitoring)
    @@ -155,7 +155,7 @@
    - 6. Security Practices (25) + 6. אבטחה (27)   [6.1. Embrace linter security rules](#-61-embrace-linter-security-rules)
    @@ -190,7 +190,7 @@
    - 7. Performance Practices (2) (Work In Progress️ ✍️) + 7. ביצועים (2) (בתהליך ✍️)   [7.1. Don't block the event loop](#-71-dont-block-the-event-loop)
    @@ -200,7 +200,7 @@
    - 8. Docker Practices (15) + 8. דוקר (15)   [8.1 Use multi-stage builds for leaner and more secure Docker images `#strategic`](#-81-use-multi-stage-builds-for-leaner-and-more-secure-docker-images)
    @@ -223,7 +223,7 @@

    -# `1. Project Architecture Practices` +# `1. מבנה הפרוייקט` ## ![✔] 1.1 Structure your solution by business components @@ -319,7 +319,7 @@ my-system

    ⬆ Return to top

    -# `2. Error Handling Practices` +# `2. ניהול שגיאות` ## ![✔] 2.1 Use Async-Await or promises for async error handling @@ -447,7 +447,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi

    ⬆ Return to top

    -# `3. Code Patterns And Style Practices` +# `3. תבניות קוד וסגנון עיצוב` ## ![✔] 3.1 Use ESLint @@ -684,7 +684,7 @@ All statements above will return false if used with `===`

    ⬆ Return to top

    -# `4. Testing And Overall Quality Practices` +# `4. בדיקות ובקרת איכות` \_We have dedicated guides for testing, see below. The best practices list here is a brief summary of these guides @@ -808,7 +808,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    ⬆ Return to top

    -# `5. Going To Production Practices` +# `5. עלייה לאוויר` ## ![✔] 5.1. Monitoring @@ -1000,7 +1000,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej

    ⬆ Return to top

    -# `6. Security Best Practices` +# `6. אבטחה`
    54 items @@ -1333,7 +1333,7 @@ This style ensures that there is no ambiguity with global npm packages and makes

    ⬆ Return to top

    -# `7. Draft: Performance Best Practices` +# `7. טיוטה: ביצועים` ## Our contributors are working on this section. [Would you like to join?](https://github.com/goldbergyoni/nodebestpractices/issues/256) @@ -1362,7 +1362,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E

    ⬆ Return to top

    -# `8. Docker Best Practices` +# `8. דוקר` 🏅 Many thanks to [Bret Fisher](https://github.com/BretFisher) from whom we learned many of the following practices From 10b2376bc76a67e09ad24a1310a461aacc83792c Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 14:23:09 +0300 Subject: [PATCH 1715/1795] translate bolded TLDR all over main readme --- README.hebrew.md | 214 +++++++++++++++++++++++------------------------ 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 5bb1e68d1..12ee5ad45 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -225,9 +225,9 @@ # `1. מבנה הפרוייקט` -## ![✔] 1.1 Structure your solution by business components +## ![✔] 1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים -**TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo +**אמ;לק:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo ```bash my-system @@ -248,7 +248,7 @@ my-system ## ![✔] 1.2 Layer your components with 3-tiers, keep the web layer within its boundaries -**TL;DR:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal +**אמ;לק:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal ```bash my-system @@ -269,7 +269,7 @@ my-system ## ![✔] 1.3 Wrap common utilities as packages, consider publishing -**TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry +**אמ;לק:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry ```bash my-system @@ -291,7 +291,7 @@ my-system ## ![✔] 1.4 Use environment aware, secure and hierarchical config -**TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others +**אמ;לק:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others **Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state @@ -301,7 +301,7 @@ my-system ## ![✔] 1.5 Consider all the consequences when choosing the main framework -**TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) +**אמ;לק:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) **Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns @@ -309,7 +309,7 @@ my-system ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully -**TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises +**אמ;לק:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises **Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time @@ -323,7 +323,7 @@ my-system ## ![✔] 2.1 Use Async-Await or promises for async error handling -**TL;DR:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch +**אמ;לק:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch **Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns @@ -333,7 +333,7 @@ my-system ## ![✔] 2.2 Extend the built-in Error object -**TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) +**אמ;לק:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! @@ -343,7 +343,7 @@ my-system ## ![✔] 2.3 Distinguish catastrophic errors from operational errors -**TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application +**אמ;לק:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application **Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context @@ -353,7 +353,7 @@ my-system ## ![✔] 2.4 Handle errors centrally, not within a middleware -**TL;DR:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in +**אמ;לק:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in **Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors @@ -363,7 +363,7 @@ my-system ## ![✔] 2.5 Document API errors using OpenAPI or GraphQL -**TL;DR:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well +**אמ;לק:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well **Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) @@ -373,7 +373,7 @@ my-system ## ![✔] 2.6 Exit the process gracefully when a stranger comes to town -**TL;DR:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart +**אמ;לק:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart **Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily @@ -383,7 +383,7 @@ my-system ## ![✔] 2.7 Use a mature logger to increase errors visibility -**TL;DR:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator +**אמ;לק:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late @@ -393,7 +393,7 @@ my-system ## ![✔] 2.8 Test error flows using your favorite test framework -**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) +**אמ;לק:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) **Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling @@ -403,7 +403,7 @@ my-system ## ![✔] 2.9 Discover errors and downtime using APM products -**TL;DR:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing +**אמ;לק:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX @@ -413,7 +413,7 @@ my-system ## ![✔] 2.10 Catch unhandled promise rejections -**TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` +**אמ;לק:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` **Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about @@ -423,7 +423,7 @@ my-system ## ![✔] 2.11 Fail fast, validate arguments using a dedicated library -**TL;DR:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) +**אמ;לק:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) **Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? @@ -433,7 +433,7 @@ my-system ## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace -**TL;DR:** Always do `return await` when returning a promise to benefit full error stacktrace. If a +**אמ;לק:** Always do `return await` when returning a promise to benefit full error stacktrace. If a function returns a promise, that function must be declared as `async` function and explicitly `await` the promise before returning it @@ -451,7 +451,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi ## ![✔] 3.1 Use ESLint -**TL;DR:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint +**אמ;לק:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint **Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style @@ -461,7 +461,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi ## ![✔] 3.2 Use Node.js eslint extension plugins -**TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules +**אמ;לק:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules **Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early @@ -469,7 +469,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi ## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line -**TL;DR:** The opening curly braces of a code block should be on the same line as the opening statement +**אמ;לק:** The opening curly braces of a code block should be on the same line as the opening statement ### Code Example @@ -495,7 +495,7 @@ function someFunction() { No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. -**TL;DR:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. +**אמ;לק:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. **Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. @@ -537,7 +537,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function ## ![✔] 3.5 Name your functions -**TL;DR:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot +**אמ;לק:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot **Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions @@ -545,7 +545,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function ## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes -**TL;DR:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short +**אמ;לק:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short **Otherwise:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase @@ -586,7 +586,7 @@ function doSomething() { ## ![✔] 3.7 Prefer const over let. Ditch the var -**TL;DR:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal +**אמ;לק:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal **Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes @@ -596,7 +596,7 @@ function doSomething() { ## ![✔] 3.8 Require modules first, not inside functions -**TL;DR:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems +**אמ;לק:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems **Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function @@ -604,7 +604,7 @@ function doSomething() { ## ![✔] 3.9 Set an explicit entry point to a module/folder -**TL;DR:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality +**אמ;לק:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality **Otherwise:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract @@ -629,7 +629,7 @@ const { SMSWithMedia } = require("./SMSProvider"); ## ![✔] 3.10 Use the `===` operator -**TL;DR:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal +**אמ;לק:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal **Otherwise:** Unequal variables might return true when compared with the `==` operator @@ -656,7 +656,7 @@ All statements above will return false if used with `===` ## ![✔] 3.11 Use Async Await, avoid callbacks -**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch +**אמ;לק:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch **Otherwise:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow @@ -666,7 +666,7 @@ All statements above will return false if used with `===` ## ![✔] 3.12 Use arrow function expressions (=>) -**TL;DR:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) +**אמ;לק:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) **Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read @@ -676,7 +676,7 @@ All statements above will return false if used with `===` ## ![✔] 3.13 Avoid effects outside of functions -**TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns +**אמ;לק:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns **Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data @@ -694,7 +694,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.1 At the very least, write API (component) testing -**TL;DR:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc +**אמ;לק:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc **Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage @@ -702,7 +702,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.2 Include 3 parts in each test name -**TL;DR:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result +**אמ;לק:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result **Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? @@ -712,7 +712,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.3 Structure tests by the AAA pattern -**TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan +**אמ;לק:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan **Otherwise:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain @@ -722,7 +722,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.4 Ensure Node version is unified -**TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) +**אמ;לק:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) **Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed @@ -730,7 +730,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test -**TL;DR:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records +**אמ;לק:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records **Otherwise:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build @@ -740,7 +740,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.6 Tag your tests -**TL;DR:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' +**אמ;לק:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' **Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests @@ -748,7 +748,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.7 Check your test coverage, it helps to identify wrong test patterns -**TL;DR:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold +**אמ;לק:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold **Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing @@ -756,7 +756,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.8 Use production-like environment for e2e testing -**TL;DR:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) +**אמ;לק:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) **Otherwise:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments @@ -764,7 +764,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.9 Refactor regularly using static analysis tools -**TL;DR:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). +**אמ;לק:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). **Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix @@ -774,7 +774,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.10 Mock responses of external HTTP services -**TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production +**אמ;לק:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production **Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow @@ -782,7 +782,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.11 Test your middlewares in isolation -**TL;DR:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects +**אמ;לק:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects **Otherwise:** A bug in Express middleware === a bug in all or most requests @@ -790,7 +790,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.12 Specify a port in production, randomize in testing -**TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port +**אמ;לק:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port **Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default @@ -798,7 +798,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.13 Test the five possible outcomes -**TL;DR:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +**אמ;לק:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) **Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) @@ -812,7 +812,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.1. Monitoring -**TL;DR:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions +**אמ;לק:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions **Otherwise:** Failure === disappointed customers. Simple @@ -822,7 +822,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.2. Increase the observability using smart logging -**TL;DR:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted +**אמ;לק:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted **Otherwise:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information @@ -832,7 +832,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy -**TL;DR:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead +**אמ;לק:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead **Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly @@ -842,7 +842,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.4. Lock dependencies -**TL;DR:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical +**אמ;לק:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code @@ -852,7 +852,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.5. Guard process uptime using the right tool -**TL;DR:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location +**אמ;לק:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location **Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos @@ -862,7 +862,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.6. Utilize all CPU cores -**TL;DR:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) +**אמ;לק:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) **Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) @@ -872,7 +872,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.7. Create a ‘maintenance endpoint’ -**TL;DR:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code +**אמ;לק:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code **Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes @@ -882,7 +882,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.8. Discover the unknowns using APM products -**TL;DR:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example +**אמ;לק:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX @@ -892,7 +892,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.9. Make your code production-ready -**TL;DR:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') +**אמ;לק:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') **Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written @@ -902,7 +902,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.10. Measure and guard the memory usage -**TL;DR:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system +**אמ;לק:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system **Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) @@ -912,7 +912,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.11. Get your frontend assets out of Node -**TL;DR:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering +**אמ;לק:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering **Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content @@ -922,7 +922,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.12. Strive to be stateless -**TL;DR:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically +**אמ;לק:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically **Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server @@ -932,7 +932,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.13. Use tools that automatically detect vulnerabilities -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**אמ;לק:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately **Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious @@ -942,7 +942,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.14. Assign a transaction id to each log statement -**TL;DR:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside +**אמ;לק:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside **Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue @@ -952,7 +952,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.15. Set `NODE_ENV=production` -**TL;DR:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production +**אמ;לק:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production **Otherwise:** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering @@ -962,7 +962,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.16. Design automated, atomic and zero-downtime deployments -**TL;DR:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment +**אמ;לק:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment **Otherwise:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features @@ -970,7 +970,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.17. Use an LTS release of Node.js -**TL;DR:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements +**אמ;לק:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements **Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain @@ -980,7 +980,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.18. Log to stdout, avoid specifying log destination within the app -**TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). +**אמ;לק:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). **Otherwise:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive @@ -990,7 +990,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.19. Install your packages with `npm ci` -**TL;DR:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files +**אמ;לק:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. @@ -1010,7 +1010,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter +**אמ;לק:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter **Otherwise:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories @@ -1022,7 +1022,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) +**אמ;לק:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) **Otherwise:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. @@ -1034,7 +1034,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally +**אמ;לק:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally **Otherwise:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). @@ -1046,7 +1046,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. +**אמ;לק:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. **Otherwise:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. @@ -1056,7 +1056,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 6.5. Collection of generic security best practices -**TL;DR:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. +**אמ;לק:** This is a collection of security advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. 🔗 [**Read More: Common security best practices**](./sections/security/commonsecuritybestpractices.md) @@ -1066,7 +1066,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). +**אמ;לק:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). **Otherwise:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities @@ -1078,7 +1078,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. +**אמ;לק:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. **Otherwise:** An attacker could detect your web framework and attack all its known vulnerabilities. @@ -1090,7 +1090,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Passwords or secrets (e.g. API keys) should be stored using a secure hash + salt function like `bcrypt`,`scrypt`, or worst case `pbkdf2`. +**אמ;לק:** Passwords or secrets (e.g. API keys) should be stored using a secure hash + salt function like `bcrypt`,`scrypt`, or worst case `pbkdf2`. **Otherwise:** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. @@ -1102,7 +1102,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) +**אמ;לק:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) **Otherwise:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients @@ -1114,7 +1114,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) +**אמ;לק:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) **Otherwise:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application @@ -1126,7 +1126,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blocklist of untrusted tokens that are validated on each request. +**אמ;לק:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blocklist of untrusted tokens that are validated on each request. **Otherwise:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. @@ -1138,7 +1138,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** A simple and powerful technique is to limit authorization attempts using two metrics: +**אמ;לק:** A simple and powerful technique is to limit authorization attempts using two metrics: 1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. 2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. @@ -1153,7 +1153,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" +**אמ;לק:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" **Otherwise:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) @@ -1165,7 +1165,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads +**אמ;לק:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads **Otherwise:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks @@ -1177,7 +1177,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. +**אמ;לק:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. **Otherwise:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. @@ -1189,7 +1189,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns +**אמ;לק:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns **Otherwise:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 @@ -1201,7 +1201,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough +**אמ;לק:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough **Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. @@ -1213,7 +1213,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox +**אמ;לק:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox **Otherwise:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables @@ -1225,7 +1225,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. +**אמ;לק:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. **Otherwise:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. @@ -1237,7 +1237,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details +**אמ;לק:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details **Otherwise:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace @@ -1249,7 +1249,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. +**אמ;לק:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. **Otherwise:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) @@ -1259,7 +1259,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) +**אמ;לק:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) **Otherwise:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities @@ -1271,7 +1271,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) +**אמ;לק:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) **Otherwise:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease @@ -1281,7 +1281,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. +**אמ;לק:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. **Otherwise:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. @@ -1293,7 +1293,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to ignore specific files or folders, or the `files` array in `package.json` can act as an allow list. +**אמ;לק:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to ignore specific files or folders, or the `files` array in `package.json` can act as an allow list. **Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. @@ -1303,7 +1303,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 6.26 Inspect for outdated packages -**TL;DR:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version +**אמ;לק:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version **Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky @@ -1313,7 +1313,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej -**TL;DR:** Import or require built-in Node.js modules using the 'node protocol' syntax: +**אמ;לק:** Import or require built-in Node.js modules using the 'node protocol' syntax: ```javascript import { functionName } from "node:module"; // note that 'node:' prefix @@ -1341,7 +1341,7 @@ This style ensures that there is no ambiguity with global npm packages and makes ## ![✔] 7.1. Don't block the event loop -**TL;DR:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. +**אמ;לק:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. **Otherwise:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** @@ -1351,7 +1351,7 @@ This style ensures that there is no ambiguity with global npm packages and makes ## ![✔] 7.2. Prefer native JS methods over user-land utils like Lodash -**TL;DR:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. +**אמ;לק:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. **Otherwise:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. @@ -1370,7 +1370,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E ## ![✔] 8.1 Use multi-stage builds for leaner and more secure Docker images -**TL;DR:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. +**אמ;לק:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. **Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. @@ -1400,7 +1400,7 @@ CMD [ "node", "dist/app.js" ] ## ![✔] 8.2. Bootstrap using `node` command, avoid `npm start` -**TL;DR:** Use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-processes, signal handling, graceful shutdown and having zombie processes +**אמ;לק:** Use `CMD ['node','server.js']` to start your app, avoid using npm scripts which don't pass OS signals to the code. This prevents problems with child-processes, signal handling, graceful shutdown and having zombie processes Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm/changelog#706-2020-10-27) to pass signals. We follow and will update accordingly @@ -1412,7 +1412,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.3. Let the Docker runtime handle replication and uptime -**TL;DR:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes +**אמ;לק:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes **Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance @@ -1432,7 +1432,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.5. Clean-up dependencies before production -**TL;DR:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` +**אמ;לק:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` **Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) @@ -1442,7 +1442,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.6. Shutdown smartly and gracefully -**TL;DR:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources +**אמ;לק:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources **Otherwise:** Dying immediately means not responding to thousands of disappointed users @@ -1452,7 +1452,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.7. Set memory limits using both Docker and v8 -**TL;DR:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit +**אמ;לק:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit **Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources @@ -1462,7 +1462,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.8. Plan for efficient caching -**TL;DR:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. +**אמ;לק:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. **Otherwise:** Docker build will be very long and consume lot of resources even when making tiny changes @@ -1472,7 +1472,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm ## ![✔] 8.9. Use explicit image reference, avoid `latest` tag -**TL;DR:** Specify an explicit image digest or versioned label, never refer to `latest`. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. +**אמ;לק:** Specify an explicit image digest or versioned label, never refer to `latest`. Developers are often led to believe that specifying the `latest` tag will provide them with the most recent image in the repository however this is not the case. Using a digest guarantees that every instance of the service is running exactly the same code. In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. @@ -1484,7 +1484,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.10. Prefer smaller Docker base images -**TL;DR:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. +**אמ;לק:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. **Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. @@ -1494,7 +1494,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args -**TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces +**אמ;לק:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces **Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus @@ -1504,7 +1504,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.12. Scan images for multi layers of vulnerabilities -**TL;DR:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins +**אמ;לק:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins **Otherwise:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications @@ -1514,7 +1514,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.13 Clean NODE_MODULE cache -**TL;DR:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off +**אמ;לק:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off **Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used @@ -1524,7 +1524,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.14. Generic Docker practices -**TL;DR:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. +**אמ;לק:** This is a collection of Docker advice that is not related directly to Node.js - the Node implementation is not much different than any other language. Click read more to skim through. 🔗 [**Read More: Generic Docker practices**](./sections/docker/generic-tips.md) @@ -1532,7 +1532,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.15. Lint your Dockerfile -**TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. +**אמ;לק:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. **Otherwise:** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. From faecc790d1d40abafcd428349191b8678132d6d8 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 22:04:45 +0300 Subject: [PATCH 1716/1795] translate main readme 1.1 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 12ee5ad45..174a9597c 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -55,7 +55,7 @@ 1. מבנה הפרוייקט (6) -  [1.1 Structure your solution by components `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    +  [1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
      [1.2 Layer your components, keep the web layer within its boundaries `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as packages, consider publishing](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
    @@ -227,7 +227,7 @@ ## ![✔] 1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים -**אמ;לק:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo +**אמ;לק:** בסיס המערכת צריך לכלול תיקיות או מאגרים שמייצג בצורה הגיונית את המידול העסקי. כל רכיב מייצג תחום מוצר (כלומר הקשר מוגבל), למשל 'משתמשים', 'הזמנות', וכולי... כל רכיב מכיל את ה API, לוגיקה ומסד הנתונים שלו. מה המטרה של זה? כאשר יש סביבה עצמאית כל שינוי משפיע אך ורק על החלק הרלוונטי - העומס הנפשי, סיבוכיות הפיתוח והחשש מפריסה חדשה של הרכיב הרבה יותר קטן. כתוצאה מכך, מתכנתים יכולים לפתח הרבה יותר מהר. זה לא דורש בהכרח הפרדה פיזית ויכול להיות מושג גם בMonorepo או multi-repo. ```bash my-system @@ -240,9 +240,9 @@ my-system │ ├─ authenticator ``` -**Otherwise:** when artifacts from various modules/topics are mixed together, there are great chances of a tightly-coupled 'spaghetti' system. For example, in an architecture where 'module-a controller' might call 'module-b service', there are no clear modularity borders - every code change might affect anything else. With this approach, developers who code new features struggle to realize the scope and impact of their change. Consequently, they fear breaking other modules, and each deployment becomes slower and riskier +**אחרת:** כאשר מוצרים של מודולים או נושאים שונים מעורבבים יחד, ישנו סיכוי גבוה שתיווצר מערכת ספגטי בעלת צימוד גבוה. לדוגמה, בארכיטקטורה שבה 'מודול א`' קורא לשירות מ'מודול ב;', אין הפרדה ברורהבין המודולים השונים - כל שינוי בקוד עלול להשפיע על משהו אחר. עם הגישה הזאת , מתכנתים שצריכים להוסיף מוצר חדש למערכת יאבקו בה בהבנה על מה השינוי שלהם יכול להשפיע. כתוצאה מכך, הם חששו לשבור מודולים אחרים, וכל פריסה נהייתה איטית יותר ומסוכנת יותר. -🔗 [**Read More: structure by components**](./sections/projectstructre/breakintcomponents.md) +🔗 [**לקריאה נוספת: בנייה לפי רכיבים**](./sections/projectstructre/breakintcomponents.md)

    From 89d310cd310ec88367486c190bb782ff2712ce28 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 20 Jun 2023 22:05:39 +0300 Subject: [PATCH 1717/1795] translate otherwise keyword --- README.hebrew.md | 206 +++++++++++++++++++++++------------------------ 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 174a9597c..2c1b1b4e7 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -261,7 +261,7 @@ my-system │ ├─ data-access # DB calls w/o ORM ``` -**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc +**:אחרת** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc 🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) @@ -283,7 +283,7 @@ my-system ``` -**Otherwise:** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface +**:אחרת** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface 🔗 [**Read More: Structure by feature**](./sections/projectstructre/wraputilities.md) @@ -293,7 +293,7 @@ my-system **אמ;לק:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others -**Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state +**:אחרת** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state 🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) @@ -303,7 +303,7 @@ my-system **אמ;לק:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) -**Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns +**:אחרת** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns 🔗 [**Read More: Choosing the right framework**](./sections/projectstructre/choose-framework.md) @@ -311,7 +311,7 @@ my-system **אמ;לק:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises -**Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time +**:אחרת** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time 🔗 [**Read More: TypeScript considerations**](./sections/projectstructre/typescript-considerations.md) @@ -325,7 +325,7 @@ my-system **אמ;לק:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch -**Otherwise:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns +**:אחרת** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns 🔗 [**Read More: avoiding callbacks**](./sections/errorhandling/asyncerrorhandling.md) @@ -335,7 +335,7 @@ my-system **אמ;לק:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) -**Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! +**:אחרת** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! 🔗 [**Read More: using the built-in error object**](./sections/errorhandling/useonlythebuiltinerror.md) @@ -345,7 +345,7 @@ my-system **אמ;לק:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application -**Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context +**:אחרת** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context 🔗 [**Read More: operational vs programmer error**](./sections/errorhandling/operationalvsprogrammererror.md) @@ -355,7 +355,7 @@ my-system **אמ;לק:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in -**Otherwise:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors +**:אחרת** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors 🔗 [**Read More: handling errors in a centralized place**](./sections/errorhandling/centralizedhandling.md) @@ -365,7 +365,7 @@ my-system **אמ;לק:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well -**Otherwise:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) +**:אחרת** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) 🔗 [**Read More: documenting API errors in Swagger or GraphQL**](./sections/errorhandling/documentingusingswagger.md) @@ -375,7 +375,7 @@ my-system **אמ;לק:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart -**Otherwise:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily +**:אחרת** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily 🔗 [**Read More: shutting the process**](./sections/errorhandling/shuttingtheprocess.md) @@ -385,7 +385,7 @@ my-system **אמ;לק:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator -**Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late +**:אחרת** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late 🔗 [**Read More: using a mature logger**](./sections/errorhandling/usematurelogger.md) @@ -395,7 +395,7 @@ my-system **אמ;לק:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) -**Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling +**:אחרת** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling 🔗 [**Read More: testing error flows**](./sections/errorhandling/testingerrorflows.md) @@ -405,7 +405,7 @@ my-system **אמ;לק:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing -**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX +**:אחרת** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX 🔗 [**Read More: using APM products**](./sections/errorhandling/apmproducts.md) @@ -415,7 +415,7 @@ my-system **אמ;לק:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` -**Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about +**:אחרת** Your errors will get swallowed and leave no trace. Nothing to worry about 🔗 [**Read More: catching unhandled promise rejection**](./sections/errorhandling/catchunhandledpromiserejection.md) @@ -425,7 +425,7 @@ my-system **אמ;לק:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) -**Otherwise:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? +**:אחרת** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? 🔗 [**Read More: failing fast**](./sections/errorhandling/failfast.md) @@ -437,7 +437,7 @@ my-system function returns a promise, that function must be declared as `async` function and explicitly `await` the promise before returning it -**Otherwise:** The function that returns a promise without awaiting won't appear in the stacktrace. +**:אחרת** The function that returns a promise without awaiting won't appear in the stacktrace. Such missing frames would probably complicate the understanding of the flow that leads to the error, especially if the cause of the abnormal behavior is inside of the missing function @@ -453,7 +453,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi **אמ;לק:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint -**Otherwise:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style +**:אחרת** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style 🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) @@ -463,7 +463,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi **אמ;לק:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules -**Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early +**:אחרת** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early

    @@ -485,7 +485,7 @@ function someFunction() { } ``` -**Otherwise:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: +**:אחרת** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: 🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) @@ -497,7 +497,7 @@ No matter if you use semicolons or not to separate your statements, knowing the **אמ;לק:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. -**Otherwise:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. +**:אחרת** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. ### Code example @@ -539,7 +539,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function **אמ;לק:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot -**Otherwise:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions +**:אחרת** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions

    @@ -547,7 +547,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function **אמ;לק:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short -**Otherwise:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase +**:אחרת** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase ### 3.6 Code Example @@ -588,7 +588,7 @@ function doSomething() { **אמ;לק:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal -**Otherwise:** Debugging becomes way more cumbersome when following a variable that frequently changes +**:אחרת** Debugging becomes way more cumbersome when following a variable that frequently changes 🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) @@ -598,7 +598,7 @@ function doSomething() { **אמ;לק:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems -**Otherwise:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function +**:אחרת** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function

    @@ -606,7 +606,7 @@ function doSomething() { **אמ;לק:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality -**Otherwise:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract +**:אחרת** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract ### 3.9 Code example - avoid coupling the client to the module structure @@ -631,7 +631,7 @@ const { SMSWithMedia } = require("./SMSProvider"); **אמ;לק:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal -**Otherwise:** Unequal variables might return true when compared with the `==` operator +**:אחרת** Unequal variables might return true when compared with the `==` operator ### 3.10 Code example @@ -658,7 +658,7 @@ All statements above will return false if used with `===` **אמ;לק:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch -**Otherwise:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow +**:אחרת** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow 🔗[**Read more:** Guide to async-await 1.0](https://github.com/yortus/asyncawait) @@ -668,7 +668,7 @@ All statements above will return false if used with `===` **אמ;לק:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) -**Otherwise:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read +**:אחרת** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read 🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) @@ -678,7 +678,7 @@ All statements above will return false if used with `===` **אמ;לק:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns -**Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data +**:אחרת** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data


    @@ -696,7 +696,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc -**Otherwise:** You may spend long days on writing unit tests to find out that you got only 20% system coverage +**:אחרת** You may spend long days on writing unit tests to find out that you got only 20% system coverage

    @@ -704,7 +704,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result -**Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? +**:אחרת** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? 🔗 [**Read More: Include 3 parts in each test name**](./sections/testingandquality/3-parts-in-name.md) @@ -714,7 +714,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan -**Otherwise:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain +**:אחרת** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain 🔗 [**Read More: Structure tests by the AAA pattern**](./sections/testingandquality/aaa.md) @@ -724,7 +724,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) -**Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed +**:אחרת** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed

    @@ -732,7 +732,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records -**Otherwise:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build +**:אחרת** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build 🔗 [**Read More: Avoid global test fixtures**](./sections/testingandquality/avoid-global-test-fixture.md) @@ -742,7 +742,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' -**Otherwise:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests +**:אחרת** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests

    @@ -750,7 +750,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold -**Otherwise:** There won't be any automated metric telling you when a large portion of your code is not covered by testing +**:אחרת** There won't be any automated metric telling you when a large portion of your code is not covered by testing

    @@ -758,7 +758,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) -**Otherwise:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments +**:אחרת** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments

    @@ -766,7 +766,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). -**Otherwise:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix +**:אחרת** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix 🔗 [**Read More: Refactoring!**](./sections/testingandquality/refactoring.md) @@ -776,7 +776,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production -**Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow +**:אחרת** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow 🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) @@ -784,7 +784,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects -**Otherwise:** A bug in Express middleware === a bug in all or most requests +**:אחרת** A bug in Express middleware === a bug in all or most requests 🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) @@ -792,7 +792,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port -**Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default +**:אחרת** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default 🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) @@ -800,7 +800,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -**Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) +**:אחרת** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) 🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) @@ -814,7 +814,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions -**Otherwise:** Failure === disappointed customers. Simple +**:אחרת** Failure === disappointed customers. Simple 🔗 [**Read More: Monitoring!**](./sections/production/monitoring.md) @@ -824,7 +824,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted -**Otherwise:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +**:אחרת** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information 🔗 [**Read More: Increase transparency using smart logging**](./sections/production/smartlogging.md) @@ -834,7 +834,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead -**Otherwise:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly +**:אחרת** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly 🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](./sections/production/delegatetoproxy.md) @@ -844,7 +844,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical -**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**:אחרת** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code 🔗 [**Read More: Lock dependencies**](./sections/production/lockdependencies.md) @@ -854,7 +854,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location -**Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos +**:אחרת** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos 🔗 [**Read More: Guard process uptime using the right tool**](./sections/production/guardprocess.md) @@ -864,7 +864,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) -**Otherwise:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) +**:אחרת** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) 🔗 [**Read More: Utilize all CPU cores**](./sections/production/utilizecpu.md) @@ -874,7 +874,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code -**Otherwise:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes +**:אחרת** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes 🔗 [**Read More: Create a ‘maintenance endpoint’**](./sections/production/createmaintenanceendpoint.md) @@ -884,7 +884,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example -**Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX +**:אחרת** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX 🔗 [**Read More: Discover errors and downtime using APM products**](./sections/production/apmproducts.md) @@ -894,7 +894,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') -**Otherwise:** A world champion IT/DevOps guy won’t save a system that is badly written +**:אחרת** A world champion IT/DevOps guy won’t save a system that is badly written 🔗 [**Read More: Make your code production-ready**](./sections/production/productioncode.md) @@ -904,7 +904,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system -**Otherwise:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) +**:אחרת** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) 🔗 [**Read More: Measure and guard the memory usage**](./sections/production/measurememory.md) @@ -914,7 +914,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering -**Otherwise:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content +**:אחרת** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content 🔗 [**Read More: Get your frontend assets out of Node**](./sections/production/frontendout.md) @@ -924,7 +924,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically -**Otherwise:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server +**:אחרת** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server 🔗 [**Read More: Be stateless, kill your Servers almost every day**](./sections/production/bestateless.md) @@ -934,7 +934,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately -**Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious +**:אחרת** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious 🔗 [**Read More: Use tools that automatically detect vulnerabilities**](./sections/production/detectvulnerabilities.md) @@ -944,7 +944,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside -**Otherwise:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue +**:אחרת** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue 🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](./sections/production/assigntransactionid.md) @@ -954,7 +954,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production -**Otherwise:** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering +**:אחרת** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering 🔗 [**Read More: Set NODE_ENV=production**](./sections/production/setnodeenv.md) @@ -964,7 +964,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment -**Otherwise:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features +**:אחרת** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features

    @@ -972,7 +972,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements -**Otherwise:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain +**:אחרת** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain 🔗 [**Read More: Use an LTS release of Node.js**](./sections/production/LTSrelease.md) @@ -982,7 +982,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). -**Otherwise:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive +**:אחרת** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive 🔗 [**Read More: Log Routing**](./sections/production/logrouting.md) @@ -992,7 +992,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files -**Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. +**:אחרת** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. 🔗 [**Read More: Use npm ci**](./sections/production/installpackageswithnpmci.md) @@ -1012,7 +1012,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter -**Otherwise:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories +**:אחרת** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories 🔗 [**Read More: Lint rules**](./sections/security/lintrules.md) @@ -1024,7 +1024,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) -**Otherwise:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. +**:אחרת** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. 🔗 [**Read More: Implement rate limiting**](./sections/security/limitrequests.md) @@ -1036,7 +1036,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally -**Otherwise:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). +**:אחרת** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). 🔗 [**Read More: Secret management**](./sections/security/secretmanagement.md) @@ -1048,7 +1048,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. -**Otherwise:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. +**:אחרת** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. 🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](./sections/security/ormodmusage.md) @@ -1068,7 +1068,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). -**Otherwise:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities +**:אחרת** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities 🔗 [**Read More: Using secure headers in your application**](./sections/security/secureheaders.md) @@ -1080,7 +1080,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. -**Otherwise:** An attacker could detect your web framework and attack all its known vulnerabilities. +**:אחרת** An attacker could detect your web framework and attack all its known vulnerabilities. 🔗 [**Read More: Dependency security**](./sections/security/dependencysecurity.md) @@ -1092,7 +1092,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Passwords or secrets (e.g. API keys) should be stored using a secure hash + salt function like `bcrypt`,`scrypt`, or worst case `pbkdf2`. -**Otherwise:** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. +**:אחרת** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. 🔗 [**Read More: User Passwords**](./sections/security/userpasswords.md) @@ -1104,7 +1104,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) -**Otherwise:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients +**:אחרת** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients 🔗 [**Read More: Escape output**](./sections/security/escape-output.md) @@ -1116,7 +1116,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) -**Otherwise:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application +**:אחרת** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application 🔗 [**Read More: Validate incoming JSON schemas**](./sections/security/validation.md) @@ -1128,7 +1128,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blocklist of untrusted tokens that are validated on each request. -**Otherwise:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. +**:אחרת** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. 🔗 [**Read More: Blocklist JSON Web Tokens**](./sections/security/expirejwt.md) @@ -1143,7 +1143,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej 1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. 2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. -**Otherwise:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application +**:אחרת** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application 🔗 [**Read More: Login rate limiting**](./sections/security/login-rate-limit.md) @@ -1155,7 +1155,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" -**Otherwise:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) +**:אחרת** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) 🔗 [**Read More: Run Node.js as non-root user**](./sections/security/non-root-user.md) @@ -1167,7 +1167,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads -**Otherwise:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks +**:אחרת** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks 🔗 [**Read More: Limit payload size**](./sections/security/requestpayloadsizelimit.md) @@ -1179,7 +1179,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. -**Otherwise:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. +**:אחרת** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. 🔗 [**Read More: Avoid JavaScript eval statements**](./sections/security/avoideval.md) @@ -1191,7 +1191,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns -**Otherwise:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 +**:אחרת** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 🔗 [**Read More: Prevent malicious RegEx**](./sections/security/regex.md) @@ -1203,7 +1203,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough -**Otherwise:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. +**:אחרת** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. 🔗 [**Read More: Safe module loading**](./sections/security/safemoduleloading.md) @@ -1215,7 +1215,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox -**Otherwise:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables +**:אחרת** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables 🔗 [**Read More: Run unsafe code in a sandbox**](./sections/security/sandbox.md) @@ -1227,7 +1227,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. -**Otherwise:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. +**:אחרת** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. 🔗 [**Read More: Be cautious when working with child processes**](./sections/security/childprocesses.md) @@ -1239,7 +1239,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details -**Otherwise:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace +**:אחרת** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace 🔗 [**Read More: Hide error details from client**](./sections/security/hideerrors.md) @@ -1251,7 +1251,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. -**Otherwise:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) +**:אחרת** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8)

    @@ -1261,7 +1261,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) -**Otherwise:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities +**:אחרת** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities 🔗 [**Read More: Cookie and session security**](./sections/security/sessions.md) @@ -1273,7 +1273,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) -**Otherwise:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease +**:אחרת** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease

    @@ -1283,7 +1283,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. -**Otherwise:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. +**:אחרת** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. 🔗 [**Read More: Prevent unsafe redirects**](./sections/security/saferedirects.md) @@ -1295,7 +1295,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to ignore specific files or folders, or the `files` array in `package.json` can act as an allow list. -**Otherwise:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. +**:אחרת** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. 🔗 [**Read More: Avoid publishing secrets**](./sections/security/avoid_publishing_secrets.md) @@ -1305,7 +1305,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version -**Otherwise:** Your production will run packages that have been explicitly tagged by their author as risky +**:אחרת** Your production will run packages that have been explicitly tagged by their author as risky

    @@ -1327,7 +1327,7 @@ import { createServer } from "node:http"; This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to a well-trusted official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) -**Otherwise:** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them +**:אחרת** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them


    @@ -1343,7 +1343,7 @@ This style ensures that there is no ambiguity with global npm packages and makes **אמ;לק:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. -**Otherwise:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** +**:אחרת** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** 🔗 [**Read More: Do not block the event loop**](./sections/performance/block-loop.md) @@ -1354,7 +1354,7 @@ This style ensures that there is no ambiguity with global npm packages and makes **אמ;לק:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. -**Otherwise:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. +**:אחרת** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. 🔗 [**Read More: Native over user land utils**](./sections/performance/nativeoverutil.md) @@ -1372,7 +1372,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **אמ;לק:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. -**Otherwise:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. +**:אחרת** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. ### Example Dockerfile for multi-stage builds @@ -1404,7 +1404,7 @@ CMD [ "node", "dist/app.js" ] Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm/changelog#706-2020-10-27) to pass signals. We follow and will update accordingly -**Otherwise:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data +**:אחרת** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data [**Read More: Bootstrap container using node command, avoid npm start**](./sections/docker/bootstrap-using-node.md) @@ -1414,7 +1414,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes -**Otherwise:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance +**:אחרת** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance 🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](./sections/docker/restart-and-replicate-processes.md) @@ -1434,7 +1434,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` -**Otherwise:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) +**:אחרת** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) 🔗 Read More: [Remove development dependencies](./sections/docker/install-for-production.md) @@ -1444,7 +1444,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources -**Otherwise:** Dying immediately means not responding to thousands of disappointed users +**:אחרת** Dying immediately means not responding to thousands of disappointed users 🔗 [**Read More: Graceful shutdown**](./sections/docker/graceful-shutdown.md) @@ -1454,7 +1454,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit -**Otherwise:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources +**:אחרת** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources 🔗 [**Read More: Set memory limits using Docker only**](./sections/docker/memory-limit.md) @@ -1464,7 +1464,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. -**Otherwise:** Docker build will be very long and consume lot of resources even when making tiny changes +**:אחרת** Docker build will be very long and consume lot of resources even when making tiny changes 🔗 [**Read More: Leverage caching to reduce build times**](./sections/docker/use-cache-for-shorter-build-time.md) @@ -1476,7 +1476,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. -**Otherwise:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. +**:אחרת** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. 🔗 [**Read More: Understand image tags and use the "latest" tag with caution**](./sections/docker/image-tags.md) @@ -1486,7 +1486,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. -**Otherwise:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. +**:אחרת** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. 🔗 [**Read More: Prefer smaller images**](./sections/docker/smaller_base_images.md) @@ -1496,7 +1496,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces -**Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus +**:אחרת** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus 🔗 [**Read More: Clean-out build-time secrets**](./sections/docker/avoid-build-time-secrets.md) @@ -1506,7 +1506,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins -**Otherwise:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications +**:אחרת** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications 🔗 [**Read More: Scan the entire image before production**](./sections/docker/scan-images.md) @@ -1516,7 +1516,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off -**Otherwise:** The image that will get shipped to production will weigh 30% more due to files that will never get used +**:אחרת** The image that will get shipped to production will weigh 30% more due to files that will never get used 🔗 [**Read More: Clean NODE_MODULE cache**](./sections/docker/clean-cache.md) @@ -1534,7 +1534,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. -**Otherwise:** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. +**:אחרת** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. 🔗 [**Read More: Lint your Dockerfile**](./sections/docker/lint-dockerfile.md) From e373ef8d1d7840a44115fbcd1f23ff8a321d15be Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 4 Jul 2023 13:07:34 +0300 Subject: [PATCH 1718/1795] translate section 1.2 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 2c1b1b4e7..f85715a80 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -56,7 +56,7 @@   [1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    -  [1.2 Layer your components, keep the web layer within its boundaries `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
    +  [1.2 חלוקת הרכיבים ל3 שכבות, שמירה על שכבת הווב בגבולותיה `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 Wrap common utilities as packages, consider publishing](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    @@ -246,9 +246,9 @@ my-system

    -## ![✔] 1.2 Layer your components with 3-tiers, keep the web layer within its boundaries +## ![✔] 1.2 חלוקת הרכיבים ל3 שכבות, שמירה על שכבת הווב בגבולותיה -**אמ;לק:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal +**אמ;לק:** כל רכיב צריך לכלול 'שכבות' - תיקייה יעודית עם אחריות משותפת: 'entry-point' איפה שחלקי השליטה נמצאים, 'domain' איפה שהלוגיקה נמצאת ו 'data-access'. העיקרון המנחה של הארכיטקטורות המובילות בשוק הוא להפריד את האחריות הטכנית (למשל: HTTP, DB ועוד) מהלוגיקה היעודית של המוצר כך שהמתכנתים יוכלו לקודד יותר תכולות בלי לדאוג לגבי ניהול תשתיות. השמה של כל שכבה בתיקייה יעודית, שידועה גם כ-[מודל 3 השכבות](https://he.wikipedia.org/wiki/%D7%90%D7%A8%D7%9B%D7%99%D7%98%D7%A7%D7%98%D7%95%D7%A8%D7%94_%D7%A8%D7%91-%D7%A9%D7%9B%D7%91%D7%AA%D7%99%D7%AA#%D7%90%D7%A8%D7%9B%D7%99%D7%98%D7%A7%D7%98%D7%95%D7%A8%D7%AA_%D7%A9%D7%9C%D7%95%D7%A9_%D7%A9%D7%9B%D7%91%D7%95%D7%AA) ([באנגלית](https://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture)) זאת הדרך _הפשוטה_ להשיג את המטרה. ```bash my-system @@ -261,9 +261,9 @@ my-system │ ├─ data-access # DB calls w/o ORM ``` -**:אחרת** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc +**:אחרת** לעתים דחופות נתקלים בכך שהמתכנתים מעבירים אובייקטי תקשורת כדוגמת request/reqponse לפונקציות בשכבות של הלוגיקה או ניהול המידע - דבר זה פוגע בעיקרון ההפרדה וגורם לכך שבעתיד יהיה קשה יותר להנגיש את הלוגיקה לסוגי קלינטים אחרים כדוגמת: בדיקות יחידה, משימות מתוזמנות וmessage queues. -🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) +🔗 [**לקריאה נוספת: חלק את המוצר לשכבות**](./sections/projectstructre/createlayers.md)

    From 6fc5b2404698c8f86919b6f23dc7432ccbc4f6e5 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 4 Jul 2023 14:15:35 +0300 Subject: [PATCH 1719/1795] translate section 1.3 --- README.hebrew.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index f85715a80..f1baead31 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -55,9 +55,9 @@ 1. מבנה הפרוייקט (6) -  [1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
    +  [1.1 בנו את הפרוייקט לפי רכיבים עסקיים `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
      [1.2 חלוקת הרכיבים ל3 שכבות, שמירה על שכבת הווב בגבולותיה `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
    -  [1.3 Wrap common utilities as packages, consider publishing](#-13-wrap-common-utilities-as-packages-consider-publishing)
    +  [1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
      [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -225,7 +225,7 @@ # `1. מבנה הפרוייקט` -## ![✔] 1.1 בנה את הפרוייקט שלך לפי רכיבים עסקיים +## ![✔] 1.1 בנו את הפרוייקט לפי רכיבים עסקיים **אמ;לק:** בסיס המערכת צריך לכלול תיקיות או מאגרים שמייצג בצורה הגיונית את המידול העסקי. כל רכיב מייצג תחום מוצר (כלומר הקשר מוגבל), למשל 'משתמשים', 'הזמנות', וכולי... כל רכיב מכיל את ה API, לוגיקה ומסד הנתונים שלו. מה המטרה של זה? כאשר יש סביבה עצמאית כל שינוי משפיע אך ורק על החלק הרלוונטי - העומס הנפשי, סיבוכיות הפיתוח והחשש מפריסה חדשה של הרכיב הרבה יותר קטן. כתוצאה מכך, מתכנתים יכולים לפתח הרבה יותר מהר. זה לא דורש בהכרח הפרדה פיזית ויכול להיות מושג גם בMonorepo או multi-repo. @@ -261,15 +261,15 @@ my-system │ ├─ data-access # DB calls w/o ORM ``` -**:אחרת** לעתים דחופות נתקלים בכך שהמתכנתים מעבירים אובייקטי תקשורת כדוגמת request/reqponse לפונקציות בשכבות של הלוגיקה או ניהול המידע - דבר זה פוגע בעיקרון ההפרדה וגורם לכך שבעתיד יהיה קשה יותר להנגיש את הלוגיקה לסוגי קלינטים אחרים כדוגמת: בדיקות יחידה, משימות מתוזמנות וmessage queues. +**אחרת:** לעתים דחופות נתקלים בכך שהמתכנתים מעבירים אובייקטי תקשורת כדוגמת request/reqponse לפונקציות בשכבות של הלוגיקה או ניהול המידע - דבר זה פוגע בעיקרון ההפרדה וגורם לכך שבעתיד יהיה קשה יותר להנגיש את הלוגיקה לסוגי קלינטים אחרים כדוגמת: בדיקות יחידה, משימות מתוזמנות וmessage queues. 🔗 [**לקריאה נוספת: חלק את המוצר לשכבות**](./sections/projectstructre/createlayers.md)

    -## ![✔] 1.3 Wrap common utilities as packages, consider publishing +## ![✔] 1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם -**אמ;לק:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry +**אמ;לק:** מקמו את כל הכלים שאפשר לשתף אותם בתיקייה ייעודית, למשל 'libraries' וכל כלי בתיקייה פנימית נפרדת, למשל '/libraries/logger'. הפכו את הכלי לחבילה בלתי תלויה עם קובץ ה package.json שלו וזאת כדי להגדיל את הכימוס (encapsulation), ואפשרו הפצה עתידית למאגר. כאשר הפרוייקט שלכם בנוי בתצורת monorepo, כלים אלו יכולים להיות מוגדרים על ידי שימוש ב 'npm linking' לכתובת הפיזית שלהם על ידי שימוש ב ts-paths או על ידי הפצה והתקנה על ידימנהל חבילות כדוגמת 'npm registry'. ```bash my-system @@ -283,9 +283,9 @@ my-system ``` -**:אחרת** Clients of a module might import and get coupled to internal functionality of a module. With a package.json at the root, one can set a package.json.main or package.json.exports to explicitly tell which files and functions are part of the public interface +**אחרת:** צרכנים של כלי יהיו צמודים לפונקציונליות הפנימית שלו. על ידי הגדרה של package.json בשורש הכלי מישהו יכול להגדיר קובץ package.json.main או package.json.exports כדי להצהיר במפורש אילו קבצים ופונקציולניות היא חלק מהחלקים הנגישים של הכלי. -🔗 [**Read More: Structure by feature**](./sections/projectstructre/wraputilities.md) +🔗 [**לקריאה נוספת: בנייה לפי תכונה**](./sections/projectstructre/wraputilities.md)

    From 9538ae2507a00510cc77c8ae4b5f7fc7842fa2e4 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 4 Jul 2023 14:19:43 +0300 Subject: [PATCH 1720/1795] fix otherwise punctuation mark --- README.hebrew.md | 202 +++++++++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 101 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index f1baead31..94e67cfc9 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -293,7 +293,7 @@ my-system **אמ;לק:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others -**:אחרת** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state +**אחרת:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state 🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) @@ -303,7 +303,7 @@ my-system **אמ;לק:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) -**:אחרת** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns +**אחרת:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns 🔗 [**Read More: Choosing the right framework**](./sections/projectstructre/choose-framework.md) @@ -311,7 +311,7 @@ my-system **אמ;לק:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises -**:אחרת** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time +**אחרת:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time 🔗 [**Read More: TypeScript considerations**](./sections/projectstructre/typescript-considerations.md) @@ -325,7 +325,7 @@ my-system **אמ;לק:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch -**:אחרת** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns +**אחרת:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns 🔗 [**Read More: avoiding callbacks**](./sections/errorhandling/asyncerrorhandling.md) @@ -335,7 +335,7 @@ my-system **אמ;לק:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) -**:אחרת** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! +**אחרת:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! 🔗 [**Read More: using the built-in error object**](./sections/errorhandling/useonlythebuiltinerror.md) @@ -345,7 +345,7 @@ my-system **אמ;לק:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application -**:אחרת** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context +**אחרת:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context 🔗 [**Read More: operational vs programmer error**](./sections/errorhandling/operationalvsprogrammererror.md) @@ -355,7 +355,7 @@ my-system **אמ;לק:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in -**:אחרת** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors +**אחרת:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors 🔗 [**Read More: handling errors in a centralized place**](./sections/errorhandling/centralizedhandling.md) @@ -365,7 +365,7 @@ my-system **אמ;לק:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well -**:אחרת** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) +**אחרת:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) 🔗 [**Read More: documenting API errors in Swagger or GraphQL**](./sections/errorhandling/documentingusingswagger.md) @@ -375,7 +375,7 @@ my-system **אמ;לק:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart -**:אחרת** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily +**אחרת:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily 🔗 [**Read More: shutting the process**](./sections/errorhandling/shuttingtheprocess.md) @@ -385,7 +385,7 @@ my-system **אמ;לק:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator -**:אחרת** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late +**אחרת:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late 🔗 [**Read More: using a mature logger**](./sections/errorhandling/usematurelogger.md) @@ -395,7 +395,7 @@ my-system **אמ;לק:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) -**:אחרת** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling +**אחרת:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling 🔗 [**Read More: testing error flows**](./sections/errorhandling/testingerrorflows.md) @@ -405,7 +405,7 @@ my-system **אמ;לק:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing -**:אחרת** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX +**אחרת:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX 🔗 [**Read More: using APM products**](./sections/errorhandling/apmproducts.md) @@ -415,7 +415,7 @@ my-system **אמ;לק:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` -**:אחרת** Your errors will get swallowed and leave no trace. Nothing to worry about +**אחרת:** Your errors will get swallowed and leave no trace. Nothing to worry about 🔗 [**Read More: catching unhandled promise rejection**](./sections/errorhandling/catchunhandledpromiserejection.md) @@ -425,7 +425,7 @@ my-system **אמ;לק:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) -**:אחרת** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? +**אחרת:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? 🔗 [**Read More: failing fast**](./sections/errorhandling/failfast.md) @@ -437,7 +437,7 @@ my-system function returns a promise, that function must be declared as `async` function and explicitly `await` the promise before returning it -**:אחרת** The function that returns a promise without awaiting won't appear in the stacktrace. +**אחרת:** The function that returns a promise without awaiting won't appear in the stacktrace. Such missing frames would probably complicate the understanding of the flow that leads to the error, especially if the cause of the abnormal behavior is inside of the missing function @@ -453,7 +453,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi **אמ;לק:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint -**:אחרת** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style +**אחרת:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style 🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) @@ -463,7 +463,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi **אמ;לק:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules -**:אחרת** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early +**אחרת:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early

    @@ -485,7 +485,7 @@ function someFunction() { } ``` -**:אחרת** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: +**אחרת:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: 🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) @@ -497,7 +497,7 @@ No matter if you use semicolons or not to separate your statements, knowing the **אמ;לק:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. -**:אחרת** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. +**אחרת:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. ### Code example @@ -539,7 +539,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function **אמ;לק:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot -**:אחרת** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions +**אחרת:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions

    @@ -547,7 +547,7 @@ const count = 2 // it tries to run 2(), but 2 is not a function **אמ;לק:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short -**:אחרת** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase +**אחרת:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase ### 3.6 Code Example @@ -588,7 +588,7 @@ function doSomething() { **אמ;לק:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal -**:אחרת** Debugging becomes way more cumbersome when following a variable that frequently changes +**אחרת:** Debugging becomes way more cumbersome when following a variable that frequently changes 🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) @@ -598,7 +598,7 @@ function doSomething() { **אמ;לק:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems -**:אחרת** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function +**אחרת:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function

    @@ -606,7 +606,7 @@ function doSomething() { **אמ;לק:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality -**:אחרת** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract +**אחרת:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract ### 3.9 Code example - avoid coupling the client to the module structure @@ -631,7 +631,7 @@ const { SMSWithMedia } = require("./SMSProvider"); **אמ;לק:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal -**:אחרת** Unequal variables might return true when compared with the `==` operator +**אחרת:** Unequal variables might return true when compared with the `==` operator ### 3.10 Code example @@ -658,7 +658,7 @@ All statements above will return false if used with `===` **אמ;לק:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch -**:אחרת** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow +**אחרת:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow 🔗[**Read more:** Guide to async-await 1.0](https://github.com/yortus/asyncawait) @@ -668,7 +668,7 @@ All statements above will return false if used with `===` **אמ;לק:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) -**:אחרת** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read +**אחרת:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read 🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) @@ -678,7 +678,7 @@ All statements above will return false if used with `===` **אמ;לק:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns -**:אחרת** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data +**אחרת:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data


    @@ -696,7 +696,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc -**:אחרת** You may spend long days on writing unit tests to find out that you got only 20% system coverage +**אחרת:** You may spend long days on writing unit tests to find out that you got only 20% system coverage

    @@ -704,7 +704,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result -**:אחרת** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? +**אחרת:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? 🔗 [**Read More: Include 3 parts in each test name**](./sections/testingandquality/3-parts-in-name.md) @@ -714,7 +714,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan -**:אחרת** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain +**אחרת:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain 🔗 [**Read More: Structure tests by the AAA pattern**](./sections/testingandquality/aaa.md) @@ -724,7 +724,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) -**:אחרת** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed +**אחרת:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed

    @@ -732,7 +732,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records -**:אחרת** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build +**אחרת:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build 🔗 [**Read More: Avoid global test fixtures**](./sections/testingandquality/avoid-global-test-fixture.md) @@ -742,7 +742,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' -**:אחרת** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests +**אחרת:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests

    @@ -750,7 +750,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold -**:אחרת** There won't be any automated metric telling you when a large portion of your code is not covered by testing +**אחרת:** There won't be any automated metric telling you when a large portion of your code is not covered by testing

    @@ -758,7 +758,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** End to end (e2e) testing which includes live data used to be the weakest link of the CI process as it depends on multiple heavy services like DB. Use an environment which is as close to your real production environment as possible like a-continue (Missed -continue here, needs content. Judging by the **Otherwise** clause, this should mention docker-compose) -**:אחרת** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments +**אחרת:** Without docker-compose, teams must maintain a testing DB for each testing environment including developers' machines, keep all those DBs in sync so test results won't vary across environments

    @@ -766,7 +766,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). -**:אחרת** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix +**אחרת:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix 🔗 [**Read More: Refactoring!**](./sections/testingandquality/refactoring.md) @@ -776,7 +776,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production -**:אחרת** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow +**אחרת:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow 🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) @@ -784,7 +784,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects -**:אחרת** A bug in Express middleware === a bug in all or most requests +**אחרת:** A bug in Express middleware === a bug in all or most requests 🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) @@ -792,7 +792,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port -**:אחרת** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default +**אחרת:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default 🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) @@ -800,7 +800,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -**:אחרת** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) +**אחרת:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) 🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) @@ -814,7 +814,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions -**:אחרת** Failure === disappointed customers. Simple +**אחרת:** Failure === disappointed customers. Simple 🔗 [**Read More: Monitoring!**](./sections/production/monitoring.md) @@ -824,7 +824,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted -**:אחרת** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +**אחרת:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information 🔗 [**Read More: Increase transparency using smart logging**](./sections/production/smartlogging.md) @@ -834,7 +834,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead -**:אחרת** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly +**אחרת:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly 🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](./sections/production/delegatetoproxy.md) @@ -844,7 +844,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical -**:אחרת** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**אחרת:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code 🔗 [**Read More: Lock dependencies**](./sections/production/lockdependencies.md) @@ -854,7 +854,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location -**:אחרת** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos +**אחרת:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos 🔗 [**Read More: Guard process uptime using the right tool**](./sections/production/guardprocess.md) @@ -864,7 +864,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) -**:אחרת** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) +**אחרת:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) 🔗 [**Read More: Utilize all CPU cores**](./sections/production/utilizecpu.md) @@ -874,7 +874,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code -**:אחרת** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes +**אחרת:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes 🔗 [**Read More: Create a ‘maintenance endpoint’**](./sections/production/createmaintenanceendpoint.md) @@ -884,7 +884,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example -**:אחרת** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX +**אחרת:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX 🔗 [**Read More: Discover errors and downtime using APM products**](./sections/production/apmproducts.md) @@ -894,7 +894,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') -**:אחרת** A world champion IT/DevOps guy won’t save a system that is badly written +**אחרת:** A world champion IT/DevOps guy won’t save a system that is badly written 🔗 [**Read More: Make your code production-ready**](./sections/production/productioncode.md) @@ -904,7 +904,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system -**:אחרת** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) +**אחרת:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) 🔗 [**Read More: Measure and guard the memory usage**](./sections/production/measurememory.md) @@ -914,7 +914,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Serve frontend content using a specialized infrastructure (nginx, S3, CDN) because Node performance gets hurt when dealing with many static files due to its single-threaded model. One exception to this guideline is when doing server-side rendering -**:אחרת** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content +**אחרת:** Your single Node thread will be busy streaming hundreds of html/images/angular/react files instead of allocating all its resources for the task it was born for – serving dynamic content 🔗 [**Read More: Get your frontend assets out of Node**](./sections/production/frontendout.md) @@ -924,7 +924,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Store any type of _data_ (e.g. user sessions, cache, uploaded files) within external data stores. When the app holds data in-process this adds additional layer of maintenance complexity like routing users to the same instance and higher cost of restarting a process. To enforce and encourage a stateless approach, most modern runtime platforms allows 'reapp-ing' instances periodically -**:אחרת** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server +**אחרת:** Failure at a given server will result in application downtime instead of just killing a faulty machine. Moreover, scaling-out elasticity will get more challenging due to the reliance on a specific server 🔗 [**Read More: Be stateless, kill your Servers almost every day**](./sections/production/bestateless.md) @@ -934,7 +934,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately -**:אחרת** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious +**אחרת:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious 🔗 [**Read More: Use tools that automatically detect vulnerabilities**](./sections/production/detectvulnerabilities.md) @@ -944,7 +944,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Assign the same identifier, transaction-id: uuid(), to each log entry within a single request (also known as correlation-id/tracing-id/request-context). Then when inspecting errors in logs, easily conclude what happened before and after. Node has a built-in mechanism, [AsyncLocalStorage](https://nodejs.org/api/async_context.html), for keeping the same context across asynchronous calls. see code examples inside -**:אחרת** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue +**אחרת:** Looking at a production error log without the context – what happened before – makes it much harder and slower to reason about the issue 🔗 [**Read More: Assign ‘TransactionId’ to each log statement**](./sections/production/assigntransactionid.md) @@ -954,7 +954,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Set the environment variable `NODE_ENV` to ‘production’ or ‘development’ to flag whether production optimizations should get activated – some npm packages determine the current environment and optimize their code for production -**:אחרת** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering +**אחרת:** Omitting this simple property might greatly degrade performance when dealing with some specific libraries like Express server-side rendering 🔗 [**Read More: Set NODE_ENV=production**](./sections/production/setnodeenv.md) @@ -964,7 +964,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Research shows that teams who perform many deployments lower the probability of severe production issues. Fast and automated deployments that don’t require risky manual steps and service downtime significantly improve the deployment process. You should probably achieve this using Docker combined with CI tools as they became the industry standard for streamlined deployment -**:אחרת** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features +**אחרת:** Long deployments -> production downtime & human-related error -> team unconfident in making deployment -> fewer deployments and features

    @@ -972,7 +972,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Ensure you are using an LTS version of Node.js to receive critical bug fixes, security updates and performance improvements -**:אחרת** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain +**אחרת:** Newly discovered bugs or vulnerabilities could be used to exploit an application running in production, and your application may become unsupported by various modules and harder to maintain 🔗 [**Read More: Use an LTS release of Node.js**](./sections/production/LTSrelease.md) @@ -982,7 +982,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). -**:אחרת** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive +**אחרת:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive 🔗 [**Read More: Log Routing**](./sections/production/logrouting.md) @@ -992,7 +992,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files -**:אחרת** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. +**אחרת:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. 🔗 [**Read More: Use npm ci**](./sections/production/installpackageswithnpmci.md) @@ -1012,7 +1012,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Make use of security-related linter plugins such as [eslint-plugin-security](https://github.com/nodesecurity/eslint-plugin-security) to catch security vulnerabilities and issues as early as possible, preferably while they're being coded. This can help catching security weaknesses like using eval, invoking a child process or importing a module with a string literal (e.g. user input). Click 'Read more' below to see code examples that will get caught by a security linter -**:אחרת** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories +**אחרת:** What could have been a straightforward security weakness during development becomes a major issue in production. Also, the project may not follow consistent code security practices, leading to vulnerabilities being introduced, or sensitive secrets committed into remote repositories 🔗 [**Read More: Lint rules**](./sections/security/lintrules.md) @@ -1024,7 +1024,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** DOS attacks are very popular and relatively easy to conduct. Implement rate limiting using an external service such as cloud load balancers, cloud firewalls, nginx, [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible) package, or (for smaller and less critical apps) a rate-limiting middleware (e.g. [express-rate-limit](https://www.npmjs.com/package/express-rate-limit)) -**:אחרת** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. +**אחרת:** An application could be subject to an attack resulting in a denial of service where real users receive a degraded or unavailable service. 🔗 [**Read More: Implement rate limiting**](./sections/security/limitrequests.md) @@ -1036,7 +1036,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Never store plain-text secrets in configuration files or source code. Instead, make use of secret-management systems like Vault products, Kubernetes/Docker Secrets, or using environment variables. As a last resort, secrets stored in source control must be encrypted and managed (rolling keys, expiring, auditing, etc). Make use of pre-commit/push hooks to prevent committing secrets accidentally -**:אחרת** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). +**אחרת:** Source control, even for private repositories, can mistakenly be made public, at which point all secrets are exposed. Access to source control for an external party will inadvertently provide access to related systems (databases, apis, services, etc). 🔗 [**Read More: Secret management**](./sections/security/secretmanagement.md) @@ -1048,7 +1048,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** To prevent SQL/NoSQL injection and other malicious attacks, always make use of an ORM/ODM or a database library that escapes data or supports named or indexed parameterized queries, and takes care of validating user input for expected types. Never just use JavaScript template strings or string concatenation to inject values into queries as this opens your application to a wide spectrum of vulnerabilities. All the reputable Node.js data access libraries (e.g. [Sequelize](https://github.com/sequelize/sequelize), [Knex](https://github.com/tgriesser/knex), [mongoose](https://github.com/Automattic/mongoose)) have built-in protection against injection attacks. -**:אחרת** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. +**אחרת:** Unvalidated or unsanitized user input could lead to operator injection when working with MongoDB for NoSQL, and not using a proper sanitization system or ORM will easily allow SQL injection attacks, creating a giant vulnerability. 🔗 [**Read More: Query injection prevention using ORM/ODM libraries**](./sections/security/ormodmusage.md) @@ -1068,7 +1068,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Your application should be using secure headers to prevent attackers from using common attacks like cross-site scripting (XSS), clickjacking and other malicious attacks. These can be configured easily using modules like [helmet](https://www.npmjs.com/package/helmet). -**:אחרת** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities +**אחרת:** Attackers could perform direct attacks on your application's users, leading to huge security vulnerabilities 🔗 [**Read More: Using secure headers in your application**](./sections/security/secureheaders.md) @@ -1080,7 +1080,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** With the npm ecosystem it is common to have many dependencies for a project. Dependencies should always be kept in check as new vulnerabilities are found. Use tools like [npm audit](https://docs.npmjs.com/cli/audit) or [snyk](https://snyk.io/) to track, monitor and patch vulnerable dependencies. Integrate these tools with your CI setup so you catch a vulnerable dependency before it makes it to production. -**:אחרת** An attacker could detect your web framework and attack all its known vulnerabilities. +**אחרת:** An attacker could detect your web framework and attack all its known vulnerabilities. 🔗 [**Read More: Dependency security**](./sections/security/dependencysecurity.md) @@ -1092,7 +1092,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Passwords or secrets (e.g. API keys) should be stored using a secure hash + salt function like `bcrypt`,`scrypt`, or worst case `pbkdf2`. -**:אחרת** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. +**אחרת:** Passwords and secrets that are stored without using a secure function are vulnerable to brute forcing and dictionary attacks that will lead to their disclosure eventually. 🔗 [**Read More: User Passwords**](./sections/security/userpasswords.md) @@ -1104,7 +1104,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Untrusted data that is sent down to the browser might get executed instead of just being displayed, this is commonly referred as a cross-site-scripting (XSS) attack. Mitigate this by using dedicated libraries that explicitly mark the data as pure content that should never get executed (i.e. encoding, escaping) -**:אחרת** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients +**אחרת:** An attacker might store malicious JavaScript code in your DB which will then be sent as-is to the poor clients 🔗 [**Read More: Escape output**](./sections/security/escape-output.md) @@ -1116,7 +1116,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Validate the incoming requests' body payload and ensure it meets expectations, fail fast if it doesn't. To avoid tedious validation coding within each route you may use lightweight JSON-based validation schemas such as [jsonschema](https://www.npmjs.com/package/jsonschema) or [joi](https://www.npmjs.com/package/joi) -**:אחרת** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application +**אחרת:** Your generosity and permissive approach greatly increases the attack surface and encourages the attacker to try out many inputs until they find some combination to crash the application 🔗 [**Read More: Validate incoming JSON schemas**](./sections/security/validation.md) @@ -1128,7 +1128,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When using JSON Web Tokens (for example, with [Passport.js](https://github.com/jaredhanson/passport)), by default there's no mechanism to revoke access from issued tokens. Once you discover some malicious user activity, there's no way to stop them from accessing the system as long as they hold a valid token. Mitigate this by implementing a blocklist of untrusted tokens that are validated on each request. -**:אחרת** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. +**אחרת:** Expired, or misplaced tokens could be used maliciously by a third party to access an application and impersonate the owner of the token. 🔗 [**Read More: Blocklist JSON Web Tokens**](./sections/security/expirejwt.md) @@ -1143,7 +1143,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej 1. The first is number of consecutive failed attempts by the same user unique ID/name and IP address. 2. The second is number of failed attempts from an IP address over some long period of time. For example, block an IP address if it makes 100 failed attempts in one day. -**:אחרת** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application +**אחרת:** An attacker can issue unlimited automated password attempts to gain access to privileged accounts on an application 🔗 [**Read More: Login rate limiting**](./sections/security/login-rate-limit.md) @@ -1155,7 +1155,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** There is a common scenario where Node.js runs as a root user with unlimited permissions. For example, this is the default behaviour in Docker containers. It's recommended to create a non-root user and either bake it into the Docker image (examples given below) or run the process on this user's behalf by invoking the container with the flag "-u username" -**:אחרת** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) +**אחרת:** An attacker who manages to run a script on the server gets unlimited power over the local machine (e.g. change iptable and re-route traffic to their server) 🔗 [**Read More: Run Node.js as non-root user**](./sections/security/non-root-user.md) @@ -1167,7 +1167,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The bigger the body payload is, the harder your single thread works in processing it. This is an opportunity for attackers to bring servers to their knees without tremendous amount of requests (DOS/DDOS attacks). Mitigate this limiting the body size of incoming requests on the edge (e.g. firewall, ELB) or by configuring [express body parser](https://github.com/expressjs/body-parser) to accept only small-size payloads -**:אחרת** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks +**אחרת:** Your application will have to deal with large requests, unable to process the other important work it has to accomplish, leading to performance implications and vulnerability towards DOS attacks 🔗 [**Read More: Limit payload size**](./sections/security/requestpayloadsizelimit.md) @@ -1179,7 +1179,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** `eval` is evil as it allows executing custom JavaScript code during run time. This is not just a performance concern but also an important security concern due to malicious JavaScript code that may be sourced from user input. Another language feature that should be avoided is `new Function` constructor. `setTimeout` and `setInterval` should never be passed dynamic JavaScript code either. -**:אחרת** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. +**אחרת:** Malicious JavaScript code finds a way into text passed into `eval` or other real-time evaluating JavaScript language functions, and will gain complete access to JavaScript permissions on the page. This vulnerability is often manifested as an XSS attack. 🔗 [**Read More: Avoid JavaScript eval statements**](./sections/security/avoideval.md) @@ -1191,7 +1191,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Regular Expressions, while being handy, pose a real threat to JavaScript applications at large, and the Node.js platform in particular. A user input for text to match might require an outstanding amount of CPU cycles to process. RegEx processing might be inefficient to an extent that a single request that validates 10 words can block the entire event loop for 6 seconds and set the CPU on 🔥. For that reason, prefer third-party validation packages like [validator.js](https://github.com/chriso/validator.js) instead of writing your own Regex patterns, or make use of [safe-regex](https://github.com/substack/safe-regex) to detect vulnerable regex patterns -**:אחרת** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 +**אחרת:** Poorly written regexes could be susceptible to Regular Expression DoS attacks that will block the event loop completely. For example, the popular `moment` package was found vulnerable with malicious RegEx usage in November of 2017 🔗 [**Read More: Prevent malicious RegEx**](./sections/security/regex.md) @@ -1203,7 +1203,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Avoid requiring/importing another file with a path that was given as parameter due to the concern that it could have originated from user input. This rule can be extended for accessing files in general (i.e. `fs.readFile()`) or other sensitive resource access with dynamic variables originating from user input. [Eslint-plugin-security](https://www.npmjs.com/package/eslint-plugin-security) linter can catch such patterns and warn early enough -**:אחרת** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. +**אחרת:** Malicious user input could find its way to a parameter that is used to require tampered files, for example, a previously uploaded file on the file system, or access already existing system files. 🔗 [**Read More: Safe module loading**](./sections/security/safemoduleloading.md) @@ -1215,7 +1215,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** When tasked to run external code that is given at run-time (e.g. plugin), use any sort of 'sandbox' execution environment that isolates and guards the main code against the plugin. This can be achieved using a dedicated process (e.g. `cluster.fork()`), serverless environment or dedicated npm packages that act as a sandbox -**:אחרת** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables +**אחרת:** A plugin can attack through an endless variety of options like infinite loops, memory overloading, and access to sensitive process environment variables 🔗 [**Read More: Run unsafe code in a sandbox**](./sections/security/sandbox.md) @@ -1227,7 +1227,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Avoid using child processes when possible and validate and sanitize input to mitigate shell injection attacks if you still have to. Prefer using `child_process.execFile` which by definition will only execute a single command with a set of attributes and will not allow shell parameter expansion. -**:אחרת** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. +**אחרת:** Naive use of child processes could result in remote command execution or shell injection attacks due to malicious user input passed to an unsanitized system command. 🔗 [**Read More: Be cautious when working with child processes**](./sections/security/childprocesses.md) @@ -1239,7 +1239,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** An integrated express error handler hides the error details by default. However, great are the chances that you implement your own error handling logic with custom Error objects (considered by many as a best practice). If you do so, ensure not to return the entire Error object to the client, which might contain some sensitive application details -**:אחרת** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace +**אחרת:** Sensitive application details such as server file paths, third party modules in use, and other internal workflows of the application which could be exploited by an attacker, could be leaked from information found in a stack trace 🔗 [**Read More: Hide error details from client**](./sections/security/hideerrors.md) @@ -1251,7 +1251,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Any step in the development chain should be protected with MFA (multi-factor authentication), npm/Yarn are a sweet opportunity for attackers who can get their hands on some developer's password. Using developer credentials, attackers can inject malicious code into libraries that are widely installed across projects and services. Maybe even across the web if published in public. Enabling 2-factor-authentication in npm leaves almost zero chances for attackers to alter your package code. -**:אחרת** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8) +**אחרת:** [Have you heard about the eslint developer whose password was hijacked?](https://medium.com/@oprearocks/eslint-backdoor-what-it-is-and-how-to-fix-the-issue-221f58f1a8c8)

    @@ -1261,7 +1261,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Each web framework and technology has its known weaknesses - telling an attacker which web framework we use is a great help for them. Using the default settings for session middlewares can expose your app to module- and framework-specific hijacking attacks in a similar way to the `X-Powered-By` header. Try hiding anything that identifies and reveals your tech stack (E.g. Node.js, express) -**:אחרת** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities +**אחרת:** Cookies could be sent over insecure connections, and an attacker might use session identification to identify the underlying framework of the web application, as well as module-specific vulnerabilities 🔗 [**Read More: Cookie and session security**](./sections/security/sessions.md) @@ -1273,7 +1273,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** The Node process will crash when errors are not handled. Many best practices even recommend to exit even though an error was caught and got handled. Express, for example, will crash on any asynchronous error - unless you wrap routes with a catch clause. This opens a very sweet attack spot for attackers who recognize what input makes the process crash and repeatedly send the same request. There's no instant remedy for this but a few techniques can mitigate the pain: Alert with critical severity anytime a process crashes due to an unhandled error, validate the input and avoid crashing the process due to invalid user input, wrap all routes with a catch and consider not to crash when an error originated within a request (as opposed to what happens globally) -**:אחרת** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease +**אחרת:** This is just an educated guess: given many Node.js applications, if we try passing an empty JSON body to all POST requests - a handful of applications will crash. At that point, we can just repeat sending the same request to take down the applications with ease

    @@ -1283,7 +1283,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Redirects that do not validate user input can enable attackers to launch phishing scams, steal user credentials, and perform other malicious actions. -**:אחרת** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. +**אחרת:** If an attacker discovers that you are not validating external, user-supplied input, they may exploit this vulnerability by posting specially-crafted links on forums, social media, and other public places to get users to click it. 🔗 [**Read More: Prevent unsafe redirects**](./sections/security/saferedirects.md) @@ -1295,7 +1295,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Precautions should be taken to avoid the risk of accidentally publishing secrets to public npm registries. An `.npmignore` file can be used to ignore specific files or folders, or the `files` array in `package.json` can act as an allow list. -**:אחרת** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. +**אחרת:** Your project's API keys, passwords or other secrets are open to be abused by anyone who comes across them, which may result in financial loss, impersonation, and other risks. 🔗 [**Read More: Avoid publishing secrets**](./sections/security/avoid_publishing_secrets.md) @@ -1305,7 +1305,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej **אמ;לק:** Use your preferred tool (e.g. `npm outdated` or [npm-check-updates](https://www.npmjs.com/package/npm-check-updates)) to detect installed outdated packages, inject this check into your CI pipeline and even make a build fail in a severe scenario. For example, a severe scenario might be when an installed package is 5 patch commits behind (e.g. local version is 1.3.1 and repository version is 1.3.8) or it is tagged as deprecated by its author - kill the build and prevent deploying this version -**:אחרת** Your production will run packages that have been explicitly tagged by their author as risky +**אחרת:** Your production will run packages that have been explicitly tagged by their author as risky

    @@ -1327,7 +1327,7 @@ import { createServer } from "node:http"; This style ensures that there is no ambiguity with global npm packages and makes it clear for the reader that the code refers to a well-trusted official module. This style can be enforced with the eslint rule ['prefer-node-protocol'](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md) -**:אחרת** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them +**אחרת:** Using the import syntax without 'node:' prefix opens the door for [typosquatting attacks](https://en.wikipedia.org/wiki/Typosquatting) where one could mistakenly mistype a module name (e.g., 'event' instead of 'events) and get a malicious package that was built only to trick users into installing them


    @@ -1343,7 +1343,7 @@ This style ensures that there is no ambiguity with global npm packages and makes **אמ;לק:** Avoid CPU intensive tasks as they will block the mostly single-threaded Event Loop and offload those to a dedicated thread, process or even a different technology based on the context. -**:אחרת** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** +**אחרת:** As the Event Loop is blocked, Node.js will be unable to handle other request thus causing delays for concurrent users. **3000 users are waiting for a response, the content is ready to be served, but one single request blocks the server from dispatching the results back** 🔗 [**Read More: Do not block the event loop**](./sections/performance/block-loop.md) @@ -1354,7 +1354,7 @@ This style ensures that there is no ambiguity with global npm packages and makes **אמ;לק:** It's often more penalising to use utility libraries like `lodash` and `underscore` over native methods as it leads to unneeded dependencies and slower performance. Bear in mind that with the introduction of the new V8 engine alongside the new ES standards, native methods were improved in such a way that it's now about 50% more performant than utility libraries. -**:אחרת** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. +**אחרת:** You'll have to maintain less performant projects where you could have simply used what was **already** available or dealt with a few more lines in exchange of a few more files. 🔗 [**Read More: Native over user land utils**](./sections/performance/nativeoverutil.md) @@ -1372,7 +1372,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E **אמ;לק:** Use multi-stage build to copy only necessary production artifacts. A lot of build-time dependencies and files are not needed for running your application. With multi-stage builds these resources can be used during build while the runtime environment contains only what's necessary. Multi-stage builds are an easy way to get rid of overweight and security threats. -**:אחרת** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. +**אחרת:** Larger images will take longer to build and ship, build-only tools might contain vulnerabilities and secrets only meant for the build phase might be leaked. ### Example Dockerfile for multi-stage builds @@ -1404,7 +1404,7 @@ CMD [ "node", "dist/app.js" ] Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm/changelog#706-2020-10-27) to pass signals. We follow and will update accordingly -**:אחרת** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data +**אחרת:** When no signals are passed, your code will never be notified about shutdowns. Without this, it will lose its chance to close properly possibly losing current requests and/or data [**Read More: Bootstrap container using node command, avoid npm start**](./sections/docker/bootstrap-using-node.md) @@ -1414,7 +1414,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** When using a Docker run time orchestrator (e.g., Kubernetes), invoke the Node.js process directly without intermediate process managers or custom code that replicate the process (e.g. PM2, Cluster module). The runtime platform has the highest amount of data and visibility for making placement decision - It knows best how many processes are needed, how to spread them and what to do in case of crashes -**:אחרת** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance +**אחרת:** Container keeps crashing due to lack of resources will get restarted indefinitely by the process manager. Should Kubernetes be aware of that, it could relocate it to a different roomy instance 🔗 [**Read More: Let the Docker orchestrator restart and replicate processes**](./sections/docker/restart-and-replicate-processes.md) @@ -1434,7 +1434,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Although Dev-Dependencies are sometimes needed during the build and test life-cycle, eventually the image that is shipped to production should be minimal and clean from development dependencies. Doing so guarantees that only necessary code is shipped and the amount of potential attacks (i.e. attack surface) is minimized. When using multi-stage build (see dedicated bullet) this can be achieved by installing all dependencies first and finally running `npm ci --production` -**:אחרת** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) +**אחרת:** Many of the infamous npm security breaches were found within development packages (e.g. [eslint-scope](https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes)) 🔗 Read More: [Remove development dependencies](./sections/docker/install-for-production.md) @@ -1444,7 +1444,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Handle the process SIGTERM event and clean-up all existing connection and resources. This should be done while responding to ongoing requests. In Dockerized runtimes, shutting down containers is not a rare event, rather a frequent occurrence that happen as part of routine work. Achieving this demands some thoughtful code to orchestrate several moving parts: The load balancer, keep-alive connections, the HTTP server and other resources -**:אחרת** Dying immediately means not responding to thousands of disappointed users +**אחרת:** Dying immediately means not responding to thousands of disappointed users 🔗 [**Read More: Graceful shutdown**](./sections/docker/graceful-shutdown.md) @@ -1454,7 +1454,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Always configure a memory limit using both Docker and the JavaScript runtime flags. The Docker limit is needed to make thoughtful container placement decision, the --v8's flag max-old-space is needed to kick off the GC on time and prevent under utilization of memory. Practically, set the v8's old space memory to be a just bit less than the container limit -**:אחרת** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources +**אחרת:** The docker definition is needed to perform thoughtful scaling decision and prevent starving other citizens. Without also defining the v8's limits, it will under utilize the container resources - Without explicit instructions it crashes when utilizing ~50-60% of its host resources 🔗 [**Read More: Set memory limits using Docker only**](./sections/docker/memory-limit.md) @@ -1464,7 +1464,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm **אמ;לק:** Rebuilding a whole docker image from cache can be nearly instantaneous if done correctly. The less updated instructions should be at the top of your Dockerfile and the ones constantly changing (like app code) should be at the bottom. -**:אחרת** Docker build will be very long and consume lot of resources even when making tiny changes +**אחרת:** Docker build will be very long and consume lot of resources even when making tiny changes 🔗 [**Read More: Leverage caching to reduce build times**](./sections/docker/use-cache-for-shorter-build-time.md) @@ -1476,7 +1476,7 @@ Update: [Starting from npm 7, npm claim](https://docs.npmjs.com/cli/v7/using-npm In addition, referring to an image tag means that the base image is subject to change, as image tags cannot be relied upon for a deterministic install. Instead, if a deterministic install is expected, a SHA256 digest can be used to reference an exact image. -**:אחרת** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. +**אחרת:** A new version of a base image could be deployed into production with breaking changes, causing unintended application behaviour. 🔗 [**Read More: Understand image tags and use the "latest" tag with caution**](./sections/docker/image-tags.md) @@ -1486,7 +1486,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Large images lead to higher exposure to vulnerabilities and increased resource consumption. Using leaner Docker images, such as Slim and Alpine Linux variants, mitigates this issue. -**:אחרת** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. +**אחרת:** Building, pushing, and pulling images will take longer, unknown attack vectors can be used by malicious actors and more resources are consumed. 🔗 [**Read More: Prefer smaller images**](./sections/docker/smaller_base_images.md) @@ -1496,7 +1496,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces -**:אחרת** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus +**אחרת:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus 🔗 [**Read More: Clean-out build-time secrets**](./sections/docker/avoid-build-time-secrets.md) @@ -1506,7 +1506,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Besides checking code dependencies vulnerabilities also scan the final image that is shipped to production. Docker image scanners check the code dependencies but also the OS binaries. This E2E security scan covers more ground and verifies that no bad guy injected bad things during the build. Consequently, it is recommended running this as the last step before deployment. There are a handful of free and commercial scanners that also provide CI/CD plugins -**:אחרת** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications +**אחרת:** Your code might be entirely free from vulnerabilities. However it might still get hacked due to vulnerable version of OS-level binaries (e.g. OpenSSL, TarBall) that are commonly being used by applications 🔗 [**Read More: Scan the entire image before production**](./sections/docker/scan-images.md) @@ -1516,7 +1516,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** After installing dependencies in a container remove the local cache. It doesn't make any sense to duplicate the dependencies for faster future installs since there won't be any further installs - A Docker image is immutable. Using a single line of code tens of MB (typically 10-50% of the image size) are shaved off -**:אחרת** The image that will get shipped to production will weigh 30% more due to files that will never get used +**אחרת:** The image that will get shipped to production will weigh 30% more due to files that will never get used 🔗 [**Read More: Clean NODE_MODULE cache**](./sections/docker/clean-cache.md) @@ -1534,7 +1534,7 @@ In addition, referring to an image tag means that the base image is subject to c **אמ;לק:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. -**:אחרת** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. +**אחרת:** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. 🔗 [**Read More: Lint your Dockerfile**](./sections/docker/lint-dockerfile.md) From f03472b08db9d7f740324c6d1fd2c843b81f1dbd Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 4 Jul 2023 14:38:33 +0300 Subject: [PATCH 1721/1795] translate section 1.4 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 94e67cfc9..72e39a86f 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -58,7 +58,7 @@   [1.1 בנו את הפרוייקט לפי רכיבים עסקיים `#strategic` `#updated`](#-11-structure-your-solution-by-business-components)
      [1.2 חלוקת הרכיבים ל3 שכבות, שמירה על שכבת הווב בגבולותיה `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם](#-13-wrap-common-utilities-as-packages-consider-publishing)
    -  [1.4 Use environment aware, secure and hierarchical config `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
    +  [1.4 השתמשו בקונפיגורציה עם משתני סביבה באופן מודע, מאובטח והיררכי `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
      [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -289,13 +289,13 @@ my-system

    -## ![✔] 1.4 Use environment aware, secure and hierarchical config +## ![✔] 1.4 השתמשו בקונפיגורציה עם משתני סביבה באופן מודע, מאובטח והיררכי -**אמ;לק:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others +**אמ;לק:** הגדרת סביבה מושלמת צריכה להבטיח כי (א) שמות משתנים יכולים להיקרא מקבצים כמו גם ממשתני סביבה (ב) סודות נשמרים מחוץ לקוד ששייך למאגר (ג) הקונפיגורציה היא היררכית לצורך חיפוש קל יותר (ד) תמיכה בסוגים שונים של משתנים (ה) וידוא מוקדם של משתנים לא תקינים (ו) הגדרת ברירת מחדל לכל שדה. ישנן מספר ספריות שעונות על רוב הדרישות הללו כמו [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), ועוד... -**אחרת:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state +**אחרת:** נניח וישנו משתנה סביבה הכרחי שלא הוגדר, המערכת תתחיל לרוץ בהצלחה, תענה לבקשות, חלק מהמידע יעודכן במסד הנתונים, ולפתע יהיה חסר לה שדה הכרחי להמשך התהליך ושבלעדיו היא לא יכולה לסיים את הפעולה, מה שייצור מערכת במצב "מלוכלך". -🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) +🔗 [**לקריאה נוספת: שיטות עבודה של קונפיגורציה**](./sections/projectstructre/configguide.md)

    From 78563c4fbec1f92084b72c4a90c095865494a554 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 4 Jul 2023 15:06:32 +0300 Subject: [PATCH 1722/1795] translate section 1.5 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 72e39a86f..56c5dd68e 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -59,7 +59,7 @@   [1.2 חלוקת הרכיבים ל3 שכבות, שמירה על שכבת הווב בגבולותיה `#strategic` `#updated`](#-12-layer-your-components-with-3-tiers-keep-the-web-layer-within-its-boundaries)
      [1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 השתמשו בקונפיגורציה עם משתני סביבה באופן מודע, מאובטח והיררכי `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
    -  [1.5 Consider all the consequences when choosing the main framework `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    +  [1.5 שקלו את כל ההשלכות בעת בחירת מסגרת `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
      [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -299,13 +299,13 @@ my-system

    -## ![✔] 1.5 Consider all the consequences when choosing the main framework +## ![✔] 1.5 שקלו את כל ההשלכות בעת בחירת מסגרת -**אמ;לק:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) +**אמ;לק:** כאשר בונים אפליקציות ו API-ים, שימוש בפריימוורק הוא חובה. קל להתעלם מהאפשרויות השונות שקיימות ומשיקולים חשובים ובסופו של דבר להשתמש באפשרות שפחות תואמת לדרישות של המוצר. נכון ל2023/2024 אנו מאמינים כי ארבעת הפריימוורקים הללו הם הכדאיים ביותר להשוואה: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), ו [Koa](https://koajs.com/). לחצו על לקריאה נוספת בהמשך כדי לקרוא פרטים נוספים בעד ונגד כל אחת מהאפשרויות. באופן פשטני, אנו מאמינים כי Node.js זאת ההתאמה הכי טובה לצוותים שרוצים לעבוד בשיטת OOP או לבנות מוצרים שמיועדים לגדול בצורה ניכרת ואי אפשר לחלק אותם לרכיבים קטנים _ועצמאיים_. ההמלצה שלנו היא Fastify עבור מערכות בגודל סבירents (כמו Microservices) שמושתתים על עקרונות פשוטים של Node.js. -**אחרת:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns +**אחרת:** בשל הכמות העצומה של השיקולים, קל לקבל החלטה על בסיס מידע חלקי ולהשוות תפוחים לתפוזים. למשל, ישנה הנחה רווחת שFastify הוא web-server מינימלי שראוי להשוות לexpress בלבד. בפועל, זהו פריימוורק עשיר עם הרבה הרחבות רשמיות שמכסות הרבה צרכים. -🔗 [**Read More: Choosing the right framework**](./sections/projectstructre/choose-framework.md) +🔗 [**לקריאה נוספת: בחירת הפריימוורק הנכון**](./sections/projectstructre/choose-framework.md) ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully From adec730981a220f57681f406b424f72c22552ac9 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Wed, 5 Jul 2023 15:21:25 +0300 Subject: [PATCH 1723/1795] translate section 1.6 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 56c5dd68e..26e00d2bf 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -60,7 +60,7 @@   [1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 השתמשו בקונפיגורציה עם משתני סביבה באופן מודע, מאובטח והיררכי `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 שקלו את כל ההשלכות בעת בחירת מסגרת `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    -  [1.6 Use TypeScript sparingly and thoughtfully `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    +  [1.6 השתמשו ב-TypeScript במידה ובצורה מושכלת `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -307,13 +307,13 @@ my-system 🔗 [**לקריאה נוספת: בחירת הפריימוורק הנכון**](./sections/projectstructre/choose-framework.md) -## ![✔] 1.6 Use TypeScript sparingly and thoughtfully +## ![✔] 1.6 השתמשו ב-TypeScript במידה ובצורה מושכלת -**אמ;לק:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises +**אמ;לק:** קידוד ללא מקדמי בטיחות של סיווג משתנים הוא כבר לא אפשרות בת קיימא, TypeScript מהווה את האפשרות הפופולרית ביותר למשימה זו. משתמשים בה להגדרת סוגי משתנים וערכי החזרה של פונקציות. עם זאת, זוהי חרב פיפיות שיכולה בקלות ליצור מורכבות בשל בסביבות 50 מילות מפתח נוספות שיש לה ותכונות מתוחכמות שצריך לדעת להשתמש בהן. שימוש בה צריך להיעשות במידה, בעדיפות להגדרות פשוטות של משתנים, ושימוש ביכולות מתקדמות רק כאשר צורך הכרחי מופיע. -**אחרת:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time +**אחרת:** [מחקרים](https://earlbarr.com/publications/typestudy.pdf) מראים כי שימוש ב-TypeScript יכול לעזור בזיהוי כ20% מהבאגים בשלבים מוקדמים יותר. ללא TypeScript חווית הפיתוח ב IDE נהיית בלתי נסבלת. מהצד השני, 80% מהבאגים היא לא עוזרת לזהות. כתוצאה מכך, שימוש בTypeScript מוסיף ערך מוגבל. רק הוספה של בדיקות איכותיות יכולה לעזור לזהות את מגוון הבאגים הרחב, כולל כאלו שנגרמים מאפיון לאתקין של סוג המשתנה. שימוש לא טוב גם עלול להרוג את המטרה, תכונות מורכבות של קוד מעלות אתמורכבות הקוד מה שבאופן ישיר מעלה את מספר הבאגים וזמן התיקון של כל באג. -🔗 [**Read More: TypeScript considerations**](./sections/projectstructre/typescript-considerations.md) +🔗 [**לקריאה נוספת: שיקולים לשימוש ב-TypeScript**](./sections/projectstructre/typescript-considerations.md)


    From adb4ab206e6ad0e48dde834c436926fe6545f3fc Mon Sep 17 00:00:00 2001 From: hodbauer Date: Wed, 5 Jul 2023 15:23:52 +0300 Subject: [PATCH 1724/1795] improve section 1.6 title --- README.hebrew.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 26e00d2bf..9c05a221b 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -60,7 +60,7 @@   [1.3 עטפו כלים משותפים בחבילות, שקלו את הפצתם](#-13-wrap-common-utilities-as-packages-consider-publishing)
      [1.4 השתמשו בקונפיגורציה עם משתני סביבה באופן מודע, מאובטח והיררכי `#updated`](#-14-use-environment-aware-secure-and-hierarchical-config)
      [1.5 שקלו את כל ההשלכות בעת בחירת מסגרת `#new`](#-15-consider-all-the-consequences-when-choosing-the-main-framework)
    -  [1.6 השתמשו ב-TypeScript במידה ובצורה מושכלת `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    +  [1.6 השתמשו ב-TypeScript במידתיות ובצורה מושכלת `#new`](#-16-use-typescript-sparingly-and-thoughtfully)
    @@ -307,11 +307,11 @@ my-system 🔗 [**לקריאה נוספת: בחירת הפריימוורק הנכון**](./sections/projectstructre/choose-framework.md) -## ![✔] 1.6 השתמשו ב-TypeScript במידה ובצורה מושכלת +## ![✔] 1.6 השתמשו ב-TypeScript במידתיות ובצורה מושכלת **אמ;לק:** קידוד ללא מקדמי בטיחות של סיווג משתנים הוא כבר לא אפשרות בת קיימא, TypeScript מהווה את האפשרות הפופולרית ביותר למשימה זו. משתמשים בה להגדרת סוגי משתנים וערכי החזרה של פונקציות. עם זאת, זוהי חרב פיפיות שיכולה בקלות ליצור מורכבות בשל בסביבות 50 מילות מפתח נוספות שיש לה ותכונות מתוחכמות שצריך לדעת להשתמש בהן. שימוש בה צריך להיעשות במידה, בעדיפות להגדרות פשוטות של משתנים, ושימוש ביכולות מתקדמות רק כאשר צורך הכרחי מופיע. -**אחרת:** [מחקרים](https://earlbarr.com/publications/typestudy.pdf) מראים כי שימוש ב-TypeScript יכול לעזור בזיהוי כ20% מהבאגים בשלבים מוקדמים יותר. ללא TypeScript חווית הפיתוח ב IDE נהיית בלתי נסבלת. מהצד השני, 80% מהבאגים היא לא עוזרת לזהות. כתוצאה מכך, שימוש בTypeScript מוסיף ערך מוגבל. רק הוספה של בדיקות איכותיות יכולה לעזור לזהות את מגוון הבאגים הרחב, כולל כאלו שנגרמים מאפיון לאתקין של סוג המשתנה. שימוש לא טוב גם עלול להרוג את המטרה, תכונות מורכבות של קוד מעלות אתמורכבות הקוד מה שבאופן ישיר מעלה את מספר הבאגים וזמן התיקון של כל באג. +**אחרת:** [מחקרים](https://earlbarr.com/publications/typestudy.pdf) מראים כי שימוש ב-TypeScript יכול לעזור בזיהוי כ20% מהבאגים בשלבים מוקדמים יותר. ללא TypeScript חווית הפיתוח ב IDE נהיית בלתי נסבלת. מהצד השני, 80% מהבאגים היא לא עוזרת לזהות. כתוצאה מכך, שימוש בTypeScript מוסיף ערך מוגבל. רק הוספה של בדיקות איכותיות יכולה לעזור לזהות את מגוון הבאגים הרחב, כולל כאלו שנגרמים מאפיון לא תקין של סוג המשתנה. שימוש לא טוב גם עלול להרוג את המטרה, תכונות מורכבות של קוד מעלות אתמורכבות הקוד מה שבאופן ישיר מעלה את מספר הבאגים וזמן התיקון של כל באג. 🔗 [**לקריאה נוספת: שיקולים לשימוש ב-TypeScript**](./sections/projectstructre/typescript-considerations.md) From fd0674ed1ec71e0c5e0616087310e5dee55cf08a Mon Sep 17 00:00:00 2001 From: hodbauer Date: Thu, 6 Jul 2023 12:23:45 +0300 Subject: [PATCH 1725/1795] translate return to top --- README.hebrew.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 9c05a221b..0586c99c1 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -317,7 +317,7 @@ my-system


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `2. ניהול שגיאות` @@ -445,7 +445,7 @@ especially if the cause of the abnormal behavior is inside of the missing functi


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `3. תבניות קוד וסגנון עיצוב` @@ -682,7 +682,7 @@ All statements above will return false if used with `===`


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `4. בדיקות ובקרת איכות` @@ -806,7 +806,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `5. עלייה לאוויר` @@ -998,7 +998,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `6. אבטחה` @@ -1331,7 +1331,7 @@ This style ensures that there is no ambiguity with global npm packages and makes


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `7. טיוטה: ביצועים` @@ -1360,7 +1360,7 @@ Bear in mind that with the introduction of the new V8 engine alongside the new E


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # `8. דוקר` @@ -1540,7 +1540,7 @@ In addition, referring to an image tag means that the base image is subject to c


    -

    ⬆ Return to top

    +

    ⬆ חזרה למעלה

    # Milestones From 17949c1ea6dfb91a10d3b68f4aca20bfe64c3575 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 9 Jul 2023 14:42:38 +0300 Subject: [PATCH 1726/1795] translate section 2.1 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 0586c99c1..7acb27a7d 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -69,7 +69,7 @@ 2. ניהול שגיאות (12) -  [2.1 Use Async-Await or promises for async error handling](#-21-use-async-await-or-promises-for-async-error-handling)
    +  [2.1 השתמשו ב Async-Await או הבטחות לניהול שגיאות אסינכרוניות](#-21-use-async-await-or-promises-for-async-error-handling)
      [2.2 Extend the built-in Error object `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    @@ -321,13 +321,13 @@ my-system # `2. ניהול שגיאות` -## ![✔] 2.1 Use Async-Await or promises for async error handling +## ![✔] 2.1 השתמשו ב Async-Await או הבטחות לניהול שגיאות אסינכרוניות -**אמ;לק:** Handling async errors in callback style is probably the fastest way to hell (a.k.a the pyramid of doom). The best gift you can give to your code is using Promises with async-await which enables a much more compact and familiar code syntax like try-catch +**אמ;לק:** ניהול שגיאות אסינכרוניות על ידי שימוש ב-callbacks זו הדרך המהירה לגהינום (הידועה בשם [פירמידת דום](https://en.wikipedia.org/wiki/Pyramid_of_doom_(programming))). המתנה הטובה ביותר שאפשר לתת לקוד הוא שימוש ב-promises בסגנון async-await דבר שמאפשר קוד הרבה יותר נקי ומסודר וסינטקס דומה ל try-catch. -**אחרת:** Node.js callback style, function(err, response), is a promising way to un-maintainable code due to the mix of error handling with casual code, excessive nesting, and awkward coding patterns +**אחרת:** סגנון הכתיבה `function(err, response)` הכולל שימוש ב-callbacks של Node.js, סולל דרך בטוחה לקוד שאי אפשר לתחזק בשל הערבוב בין ניהול שגיאות לניהול התהליך התקני של המערכת, עם קינון מוגזם וסגנון קוד מוזר. -🔗 [**Read More: avoiding callbacks**](./sections/errorhandling/asyncerrorhandling.md) +🔗 [**לקריאה נוספת: הימנעות מ-callbacks**](./sections/errorhandling/asyncerrorhandling.md)

    From fdcb0e0cb3b83751c54d3afa6d0ec5822c2b6f9a Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 9 Jul 2023 15:19:14 +0300 Subject: [PATCH 1727/1795] translate section 2.2 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 7acb27a7d..5a17ac045 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -70,7 +70,7 @@   [2.1 השתמשו ב Async-Await או הבטחות לניהול שגיאות אסינכרוניות](#-21-use-async-await-or-promises-for-async-error-handling)
    -  [2.2 Extend the built-in Error object `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
    +  [2.2 הרחיבו את מבנה אוביקט השגיאה המובנה Error `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
      [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
    @@ -331,13 +331,13 @@ my-system

    -## ![✔] 2.2 Extend the built-in Error object +## ![✔] 2.2 הרחיבו את מבנה אוביקט השגיאה המובנה `Error` -**אמ;לק:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) +**אמ;לק:** ישנן ספריות שזורקות שגיאה כמחרוזת או כאובייקט פרי מחשבת כותבי הקוד של הספריה - דבר שיוצר מורכבות בניהול השגיאות וביצירת מכנה משותף בין מודולים שונים. במקום זאת, השקיעו ביצירת אובייקט או מחלקת (class) שגיאה שיורשת מאובייקט השגיאה המובנה של השפה והשתמשו בזה בכל פעם שצריך לדחות את המצב, לזרוק שגיאה או להפיץ שגיאה. השגיאה האפליקטיבית צריכה להוסיף שדות נוספים כדוגמת שם השגיאה ורמת החומרה שלה. על ידי כך, לכל השגיאות ישנו מבנה אחיד והן מאפשרות תמיכה טובה יותר בניהול שגיאות. ישנו כלל של `no-throw-literal` ESLint שבודק בצורה מיטבית את השימוש הזה (על אף שיש לזה קצת [מגבלות](https://eslint.org/docs/rules/no-throw-literal) שיכולות להסתדר על ידי שימוש ב-TypeScript והגדרת החוק `@typescript-eslint/no-throw-literal`) -**אחרת:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! +**אחרת:** כאשר מפעילים רכיב כלשהו, אם ישנה אי וודאות איזה סוג של שגיאה יגיע - זה גורם לכך שניהול השגיאות יהיה הרבה יותר מורכב. גרוע מכך, שימוש באובייקטים מומצאים לתיאור שגיאות עלול להוביל לאיבוד של שגיאות קריטיות בעלות מידע חשוב כמו מעקב אחר מקור השגיאה! -🔗 [**Read More: using the built-in error object**](./sections/errorhandling/useonlythebuiltinerror.md) +🔗 [**לקריאה נוספת: שימוש באובייקט השגיאה המובנה**](./sections/errorhandling/useonlythebuiltinerror.md)

    From 99600d4158a8f6332875a2ed79e980ea07cac0e6 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 10 Jul 2023 13:55:34 +0300 Subject: [PATCH 1728/1795] translate section 2.3 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 5a17ac045..609b29399 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -71,7 +71,7 @@   [2.1 השתמשו ב Async-Await או הבטחות לניהול שגיאות אסינכרוניות](#-21-use-async-await-or-promises-for-async-error-handling)
      [2.2 הרחיבו את מבנה אוביקט השגיאה המובנה Error `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
    -  [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
    +  [2.3 הבחינו בין שגיאות קטסטרופליות לבין שגיאות תפעוליות `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
      [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
    @@ -341,13 +341,13 @@ my-system

    -## ![✔] 2.3 Distinguish catastrophic errors from operational errors +## ![✔] 2.3 הבחינו בין שגיאות קטסטרופליות לבין שגיאות תפעוליות -**אמ;לק:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application +**אמ;לק:** שגיאות תפעוליות (למשל קלט לא תקין בפנייה ל-API) מתייחסות למקרים ידועים בהם ההשפעה של השגיאה מובנת לחלוטין ויכולה להיות מנוהלת בצורה מחושבת. מצד שני, שגיאות קטסטרופליות (ידועות גם כשגיאות תכנות) מתייחסות לשגיאות לא צפויות במערכת שדורשות אתחול בטוח שלה. -**אחרת:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context +**אחרת:** אתם עלולים לאתחל את המערכת בעקבות כל שגיאה. אבל למה לגרום לכ-5000 משתמשים לחוות התנתקות בגלל שגיאה תפעולית צפויה ושולית? ההיפך הוא גם לא אידיאלי - להשאיר את המערכת עובדת כאשר קטסטרופה לא צפויה קרתה בה והיא עלולה לגרור התנהגות בלתי צפויה. הבדלה בין שני המקרים מאפשרת התמודדות מושכלת ומאוזנת בהתאם להקשר. -🔗 [**Read More: operational vs programmer error**](./sections/errorhandling/operationalvsprogrammererror.md) +🔗 [**לקריאה נוספת: שגיאות תפעוליות מול שגיאות תכנות**](./sections/errorhandling/operationalvsprogrammererror.md)

    From 4d9b9181499cab3543bd05f958344899b59964b1 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 11:26:51 +0300 Subject: [PATCH 1729/1795] translate section 2.4 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 609b29399..6b52566ab 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -72,7 +72,7 @@   [2.1 השתמשו ב Async-Await או הבטחות לניהול שגיאות אסינכרוניות](#-21-use-async-await-or-promises-for-async-error-handling)
      [2.2 הרחיבו את מבנה אוביקט השגיאה המובנה Error `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 הבחינו בין שגיאות קטסטרופליות לבין שגיאות תפעוליות `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
    -  [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    +  [2.4 נהלו את השגיאות במרוכז ולא באמצעות כלי ביניים `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
      [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
    @@ -351,13 +351,13 @@ my-system

    -## ![✔] 2.4 Handle errors centrally, not within a middleware +## ![✔] 2.4 נהלו את השגיאות במרוכז ולא באמצעות כלי ביניים -**אמ;לק:** Error handling logic such as logging, deciding whether to crash and monitoring metrics should be encapsulated in a dedicated and centralized object that all entry-points (e.g. APIs, cron jobs, scheduled jobs) call when an error comes in +**אמ;לק:** מימוש הניהול של השגיאות כמו למשל תעוד השגיאה, החלטה אם לקרוס ואילו מדדים לנטר צריך להיות מרוכז במקום אחד שכל הכניסות למערכת (למשל APIs, cron jobs, scheduled jobs) משתמשות בו כאשר חלה בהן שגיאה. -**אחרת:** Not handling errors within a single place will lead to code duplication and probably to improperly handled errors +**אחרת:** אם לא מנהלים את השגיאות במקום אחד אז במהרה יהיה שכפול קוד וכנראה ניהול לא תקין של חלק מהשגיאות. -🔗 [**Read More: handling errors in a centralized place**](./sections/errorhandling/centralizedhandling.md) +🔗 [**לקריאה נוספת: ניהול השגיאות במקום מרוכז**](./sections/errorhandling/centralizedhandling.md)

    From b39d0c6a2632c6787894a9144333981acacd6f57 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 11:53:48 +0300 Subject: [PATCH 1730/1795] translate section 2.5 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 6b52566ab..c2205fca0 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -73,7 +73,7 @@   [2.2 הרחיבו את מבנה אוביקט השגיאה המובנה Error `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 הבחינו בין שגיאות קטסטרופליות לבין שגיאות תפעוליות `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 נהלו את השגיאות במרוכז ולא באמצעות כלי ביניים `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    -  [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
    +  [2.5 תעדו את שגיאות ה-API באמצעות OpenAPI או GraphQL](#-25-document-api-errors-using-openapi-or-graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
    @@ -361,13 +361,13 @@ my-system

    -## ![✔] 2.5 Document API errors using OpenAPI or GraphQL +## ![✔] 2.5 תעדו את שגיאות ה-API באמצעות OpenAPI או GraphQL -**אמ;לק:** Let your API callers know which errors might come in return so they can handle these thoughtfully without crashing. For RESTful APIs, this is usually done with documentation frameworks like OpenAPI. If you're using GraphQL, you can utilize your schema and comments as well +**אמ;לק:** אפשרו למשתמשי ה-API שלכם לדעת אילו שגיאות עלולות להגיע כתשובה, כך שהם יוכלו להתמודד איתן בצורה מושכלת במקום לקרוס. ל-API מבוסס REST זה נעשה בדרך כלל באמצעות כלי תעוד כמו OpenAPI. אם אתם משתמשים ב-GraphQL, אתם יכולים להשתמש בסכמה ובהערות בשביל להשיג את המטרה. -**אחרת:** An API client might decide to crash and restart only because it received back an error it couldn’t understand. Note: the caller of your API might be you (very typical in a microservice environment) +**אחרת:** מי שמשתמש ב-API שלנו עלול להחליט לגרום למערכת שלו לקרוס ולאתחל את עצמה רק בגלל שהוא קיבל שגיאה שהוא לא הצליח להבין. שימו לב: המשתמש של ה-API שלכם יכול להיות אתם (מה שקורה הרבה כשמשתמשים במיקרוסרוויסים). -🔗 [**Read More: documenting API errors in Swagger or GraphQL**](./sections/errorhandling/documentingusingswagger.md) +🔗 [**לקריאה נוספת: תיעוד שגיאות ה-API באמצעות OpenAPI או GraphQL**](./sections/errorhandling/documentingusingswagger.md)

    From d58b79f9c104c903094865cfd24864132e1cc69d Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 12:10:05 +0300 Subject: [PATCH 1731/1795] translate section 2.6 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index c2205fca0..0d065acee 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -74,7 +74,7 @@   [2.3 הבחינו בין שגיאות קטסטרופליות לבין שגיאות תפעוליות `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 נהלו את השגיאות במרוכז ולא באמצעות כלי ביניים `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
      [2.5 תעדו את שגיאות ה-API באמצעות OpenAPI או GraphQL](#-25-document-api-errors-using-openapi-or-graphql)
    -  [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
    +  [2.6 הורידו את התהליך בצורה מסודרת כאשר זר בא לבקר `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
    @@ -371,13 +371,13 @@ my-system

    -## ![✔] 2.6 Exit the process gracefully when a stranger comes to town +## ![✔] 2.6 הורידו את התהליך בצורה מסודרת כאשר זר בא לבקר -**אמ;לק:** When an unknown error occurs (catastrophic error, see best practice 2.3) - there is uncertainty about the application healthiness. In this case, there is no escape from making the error observable, shutting off connections and exiting the process. Any reputable runtime framework like Dockerized services or cloud serverless solutions will take care to restart +**אמ;לק:** כאשר שגיאה לא ידועה חלה (שגיאה קטסטרופלית, ראו תובנה 2.3) - ישנה חוסר ודאות לגבי הבריאות והיציבות של המערכת. במקרה כזה, אין דרך לברוח מלגרום לשגיאה להיות ברת צפייה, סגירת חיבוריות לרכיבים נוספים והורדה של התהליך. כל סביבת ריצה מהימנה כדוגמת שירותי Docker או שירותי ענן שמספקים פתרונות ללא שרת (serverless) יוודאו שהתהליך יעלה מחדש עבורכם. -**אחרת:** When an unfamiliar exception occurs, some object might be in a faulty state (e.g. an event emitter which is used globally and not firing events anymore due to some internal failure) and all future requests might fail or behave crazily +**אחרת:** כאשר שגיאה לא צפויה קורית, רכיב כלשהו עלול להיות במצב לא תקין (למשל event emitter גלובאלי שמפסיק להפיץ אירועים בשל כשלון פנימי) והחל מעכשיו שאר הבקשות שמשתמשות ברכיב זה עלולות להיכשל או להתנהג באופן ממש לא צפוי. -🔗 [**Read More: shutting the process**](./sections/errorhandling/shuttingtheprocess.md) +🔗 [**לקריאה נוספת: הורדת התהליך**](./sections/errorhandling/shuttingtheprocess.md)

    From ca8834bd50f15d0461775a8a397fc5f84f91cc57 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 12:28:32 +0300 Subject: [PATCH 1732/1795] translate section 2.7 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 0d065acee..cb35b0ac6 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -75,7 +75,7 @@   [2.4 נהלו את השגיאות במרוכז ולא באמצעות כלי ביניים `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
      [2.5 תעדו את שגיאות ה-API באמצעות OpenAPI או GraphQL](#-25-document-api-errors-using-openapi-or-graphql)
      [2.6 הורידו את התהליך בצורה מסודרת כאשר זר בא לבקר `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
    -  [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
    +  [2.7 השתמשו ב-Logger מוכר ואמין כדי להגדיל את הקְרִיאוּת של השגיאות `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
    @@ -381,13 +381,13 @@ my-system

    -## ![✔] 2.7 Use a mature logger to increase errors visibility +## ![✔] 2.7 השתמשו ב-Logger מוכר ואמין כדי להגדיל את הקְרִיאוּת של השגיאות -**אמ;לק:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator +**אמ;לק:** כלי לוגים איכותי כדוגמת [Pino](https://github.com/pinojs/pino) או [Winston](https://github.com/winstonjs/winston) מגדיל את הקריאות וההבנה של הלוגים על ידי שימוש ברמת חומרה, עימוד, עיצוב, צבעים ועוד. ל-`console.log` אין את היכולות הללו וראוי להימנע משימוש בו. העיפרון החד ביותר בתחום מאפשר הוספה של שדות שימושיים נוספים ללא תקורה גבוהה של ביצועים. מפתחים צריכים לכתוב את הלוגים ל-`stdout` ולתת לתשתית להעביר את המידע לכלי המתאים עבור כל מקרה. -**אחרת:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late +**אחרת:** רפרוף על שורות console.log או בצורה ידנית על קבצי טקסט עמוסים לעייפה ללא כלי חיפוש ותצוגה מותאמים עלולים להשאיר אתכם לעבוד עד השעות הקטנות של הלילה. -🔗 [**Read More: using a mature logger**](./sections/errorhandling/usematurelogger.md) +🔗 [**לקריאה נוספת: שימוש ב-Logger אמין**](./sections/errorhandling/usematurelogger.md)

    From a876b56bac1cbcf903a7a891fb3745e2fd3012d1 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 12:39:25 +0300 Subject: [PATCH 1733/1795] translate section 2.8 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index cb35b0ac6..ee5f96bcd 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -76,7 +76,7 @@   [2.5 תעדו את שגיאות ה-API באמצעות OpenAPI או GraphQL](#-25-document-api-errors-using-openapi-or-graphql)
      [2.6 הורידו את התהליך בצורה מסודרת כאשר זר בא לבקר `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 השתמשו ב-Logger מוכר ואמין כדי להגדיל את הקְרִיאוּת של השגיאות `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
    -  [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
    +  [2.8 בידקו את תגובת המערכת לשגיאות על ידי שימוש בכלי הבדיקות האהוב עליכם `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
    @@ -391,13 +391,13 @@ my-system

    -## ![✔] 2.8 Test error flows using your favorite test framework +## ![✔] 2.8 בידקו את תגובת המערכת לשגיאות על ידי שימוש בכלי הבדיקות האהוב עליכם -**אמ;לק:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) +**אמ;לק:** בין אם יש לכם כלי QA אוטומטי ומקצועי ובין אם אחד המפתחים מבצע את הבדיקות - ודאו כי לא רק המסלול הבטוח של הקוד מכוסה, אלא גם ניהול השגיאות ושחוזרות השגיאות שאמורות לחזור במקרה של תקלה. נוסף על כך, בידקו מקרים מורכבים יותר של שגיאות, כמו למשל שגיאות בלתי צפויות, כדי לוודא שהרכיב שמטפל בשגיאות מבצע זאת כראוי (ראו דוגמאות קוד בקישור "לקריאה נוספת") -**אחרת:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling +**אחרת:** ללא בדיקות כלל, לא ידניות ולא אוטומטיות, לא תוכלו לסמוך על הקוד שלכם שיחזיר את השגיאה הנכונה. ללא שגיאות משמעותיות לא תוכלו לטפל בשגיאות. -🔗 [**Read More: testing error flows**](./sections/errorhandling/testingerrorflows.md) +🔗 [**לקריאה נוספת: בדיקת התנהגות בעת שגיאה**](./sections/errorhandling/testingerrorflows.md)

    From b04943b2b350bebe96bb40863775784c34e78bc1 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 12:49:19 +0300 Subject: [PATCH 1734/1795] translate section 2.9 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index ee5f96bcd..552b48cd6 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -77,7 +77,7 @@   [2.6 הורידו את התהליך בצורה מסודרת כאשר זר בא לבקר `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 השתמשו ב-Logger מוכר ואמין כדי להגדיל את הקְרִיאוּת של השגיאות `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 בידקו את תגובת המערכת לשגיאות על ידי שימוש בכלי הבדיקות האהוב עליכם `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
    -  [2.9 Discover errors and downtime using APM products](#-29-discover-errors-and-downtime-using-apm-products)
    +  [2.9 גלו שגיאות וזמני השבתה על ידי שימוש בכלי APM](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
      [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    @@ -401,13 +401,13 @@ my-system

    -## ![✔] 2.9 Discover errors and downtime using APM products +## ![✔] 2.9 גלו שגיאות וזמני השבתה על ידי שימוש בכלי APM -**אמ;לק:** Monitoring and performance products (a.k.a APM) proactively gauge your codebase or API so they can automagically highlight errors, crashes, and slow parts that you were missing +**אמ;לק:** כלי ניטור ובדיקת ביצועים (מוכרים כ-APM) מודדים באופן יזום את הקוד או ה-API כך שבאופן קסום הם מציגים שגיאות, התרסקויות וחלקים שעובדים לאט מהצפוי ואתם לא שמים לב אליהם. -**אחרת:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which are your slowest code parts under real-world scenario and how these affect the UX +**אחרת:** אתם עלולים להתאמץ רבות במדידה של בעיות ביצועים וזמני השבתה של המערכת, כנראה שלעולם לא תהיו מודעים לאיזה חלקים במערכת הם האיטיים ביותר ואיך זה משפיע על חווית המשתמש. -🔗 [**Read More: using APM products**](./sections/errorhandling/apmproducts.md) +🔗 [**לקריאה נוספת: שימוש ב-APM**](./sections/errorhandling/apmproducts.md)

    From 9d9cccc64a5eae48dead2ee26438b1d83028e360 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 11 Jul 2023 12:58:45 +0300 Subject: [PATCH 1735/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d811388ee..9666f9ec8 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [2.2 Extend the built-in Error object `#strategic` `#updated`](#-22-extend-the-built-in-error-object)
      [2.3 Distinguish operational vs programmer errors `#strategic` `#updated`](#-23-distinguish-catastrophic-errors-from-operational-errors)
      [2.4 Handle errors centrally, not within a middleware `#strategic`](#-24-handle-errors-centrally-not-within-a-middleware)
    -  [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or -graphql)
    +  [2.5 Document API errors using OpenAPI or GraphQL](#-25-document-api-errors-using-openapi-or-graphql)
      [2.6 Exit the process gracefully when a stranger comes to town `#strategic`](#-26-exit-the-process-gracefully-when-a-stranger-comes-to-town)
      [2.7 Use a mature logger to increase errors visibility `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 Test error flows using your favorite test framework `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
    From 9c8d237255deedb4db2476ac3ac3740d693e5d08 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 13:04:35 +0300 Subject: [PATCH 1736/1795] translate section 2.10 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 552b48cd6..c5fca7f09 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -78,7 +78,7 @@   [2.7 השתמשו ב-Logger מוכר ואמין כדי להגדיל את הקְרִיאוּת של השגיאות `#updated`](#-27-use-a-mature-logger-to-increase-errors-visibility)
      [2.8 בידקו את תגובת המערכת לשגיאות על ידי שימוש בכלי הבדיקות האהוב עליכם `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 גלו שגיאות וזמני השבתה על ידי שימוש בכלי APM](#-29-discover-errors-and-downtime-using-apm-products)
    -  [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
    +  [2.10 תפסו מקרים לא מטופלים של דחיות של הבטחות `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
      [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    @@ -411,13 +411,13 @@ my-system

    -## ![✔] 2.10 Catch unhandled promise rejections +## ![✔] 2.10 תפסו מקרים לא מטופלים של דחיות של הבטחות -**אמ;לק:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` +**אמ;לק:** כל שגיאה או דחייה שחוזרת מהבטחה תיבלע, אלא אם כן בשלב הפיתוח יטפלו בה כמו שצריך. אפילו אם יש בקוד האזנה ל `process.uncaughtException`! כדי להתגבר על זה צריך להאזין גם ל `process.unhandledRejection`. -**אחרת:** Your errors will get swallowed and leave no trace. Nothing to worry about +**אחרת:** השגיאות במערכת יבלעו ויעלמו ללא עקבות. לא משהו שצריך לדאוג ממנו... -🔗 [**Read More: catching unhandled promise rejection**](./sections/errorhandling/catchunhandledpromiserejection.md) +🔗 [**לקריאה נוספת: תפיסה של דחיות של הבטחות**](./sections/errorhandling/catchunhandledpromiserejection.md)

    From 92d2bbfb234675354765314eaf2e65e5d281c0da Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 14:38:04 +0300 Subject: [PATCH 1737/1795] translate section 2.11 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index c5fca7f09..d9cfb53fc 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -79,7 +79,7 @@   [2.8 בידקו את תגובת המערכת לשגיאות על ידי שימוש בכלי הבדיקות האהוב עליכם `#updated`](#-28-test-error-flows-using-your-favorite-test-framework)
      [2.9 גלו שגיאות וזמני השבתה על ידי שימוש בכלי APM](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 תפסו מקרים לא מטופלים של דחיות של הבטחות `#updated`](#-210-catch-unhandled-promise-rejections)
    -  [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
    +  [2.11 היכשלו מהר, ודאו את משתני הקלט באמצעות ספריה יעודית](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
      [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    @@ -421,13 +421,13 @@ my-system

    -## ![✔] 2.11 Fail fast, validate arguments using a dedicated library +## ![✔] 2.11 היכשלו מהר, ודאו את משתני הקלט באמצעות ספריה יעודית -**אמ;לק:** Assert API input to avoid nasty bugs that are much harder to track later. The validation code is usually tedious unless you are using a modern validation library like [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), or [typebox](https://github.com/sinclairzx81/typebox) +**אמ;לק:** הגדירו תבנית קלט קשיחה ל-API כדי להימנע מבאגים מלוכלכים שקשה הרבה יותר לעקוב אחריהם. כתיבת קוד האימות הוא תהליך מייגע, אלא אם כן תשתמשו באחת הספריות המוכרות כיום כמו [ajv](https://www.npmjs.com/package/ajv), [zod](https://github.com/colinhacks/zod), או [typebox](https://github.com/sinclairzx81/typebox). -**אחרת:** Consider this – your function expects a numeric argument “Discount” which the caller forgets to pass, later on, your code checks if Discount!=0 (amount of allowed discount is greater than zero), then it will allow the user to enjoy a discount. OMG, what a nasty bug. Can you see it? +**אחרת:** חשבו על זה - הפונקציה שלכם מצפה לקבל כקלט משתנה `discount` מספרי שמי שקרה לפונקציה שכח להעביר. בהמשך, הקוד בודק אם `discount != 0` (כמות ההנחה שאפשר לקבל גדולה מאפס), ואם כן אז המשתמש יהנה מההנחה. וואו, זה באג מלוכלך, ראיתם??? -🔗 [**Read More: failing fast**](./sections/errorhandling/failfast.md) +🔗 [**לקריאה נוספת: כשלון מהיר**](./sections/errorhandling/failfast.md)

    From 54b889f7771ebf417ed14e35b6da7b7cd1fec313 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 11 Jul 2023 14:49:19 +0300 Subject: [PATCH 1738/1795] translate section 2.12 --- README.hebrew.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index d9cfb53fc..6f9135ffb 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -80,7 +80,7 @@   [2.9 גלו שגיאות וזמני השבתה על ידי שימוש בכלי APM](#-29-discover-errors-and-downtime-using-apm-products)
      [2.10 תפסו מקרים לא מטופלים של דחיות של הבטחות `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 היכשלו מהר, ודאו את משתני הקלט באמצעות ספריה יעודית](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
    -  [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    +  [2.12 תמיד המתינו לתשובה מההבטחות לפני שאתם מעבירים את התשובה הלאה כדי להימנע ממעקב חלקי `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    @@ -431,17 +431,20 @@ my-system

    -## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace +## ![✔] 2.12 תמיד המתינו לתשובה מההבטחות לפני שאתם מעבירים את התשובה הלאה כדי להימנע ממעקב חלקי -**אמ;לק:** Always do `return await` when returning a promise to benefit full error stacktrace. If a -function returns a promise, that function must be declared as `async` function and explicitly -`await` the promise before returning it +**אמ;לק:** תמיד כתבו `return await` כאשר מחזירים תוצאה של הבטחה וזאת כדי להשיג ערך מלא של מעקב אחר מקור השגיאה (stacktrace). אם פונקציה מחזירה הבטחה היא חייבת להיות מוגדרת כפונקציה אסינכרונית ובמפורש לחכות להבטחה שהיא מחזירה. -**אחרת:** The function that returns a promise without awaiting won't appear in the stacktrace. -Such missing frames would probably complicate the understanding of the flow that leads to the error, -especially if the cause of the abnormal behavior is inside of the missing function +```js +async function promisifyFunction() { + // some logic + return await new Promise(...); +} +``` + +**אחרת:** הפונקציה שמחזירה הבטחה ללא המתנה לא תופיע בנתיב המעקב אחרי השגיאה (stacktrace). חוסרים כאלו עלולים לסבך את ההבנה של זרימת המערכת שגרמה לשגיאה, במיוחד אם הגורם להתנהגות הלא צפויה קרה בפונקציה החסרה. -🔗 [**Read More: returning promises**](./sections/errorhandling/returningpromises.md) +🔗 [**לקריאה נוספת: החזרת הבטחות**](./sections/errorhandling/returningpromises.md)


    From 0992f1251e31a63c963596a69da9aa36531660b2 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Wed, 12 Jul 2023 14:54:44 +0300 Subject: [PATCH 1739/1795] translate section 3.1 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 6f9135ffb..dc3276d73 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -89,7 +89,7 @@ 3. תבניות קוד וסגנון עיצוב (12) -  [3.1 Use ESLint `#strategic`](#-31-use-eslint)
    +  [3.1 השתמשו ב-ESLint `#strategic`](#-31-use-eslint)
      [3.2 Use Node.js eslint extension plugins `#updated`](#-32-use-nodejs-eslint-extension-plugins)
      [3.3 Start a Codeblock's Curly Braces on the Same Line](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
    @@ -452,13 +452,13 @@ async function promisifyFunction() { # `3. תבניות קוד וסגנון עיצוב` -## ![✔] 3.1 Use ESLint +## ![✔] 3.1 השתמשו ב-ESLint -**אמ;לק:** [ESLint](https://eslint.org) is the de-facto standard for checking possible code errors and fixing code style, not only to identify nitty-gritty spacing issues but also to detect serious code anti-patterns like developers throwing errors without classification. Though ESLint can automatically fix code styles, other tools like [prettier](https://www.npmjs.com/package/prettier) are more powerful in formatting the fix and work in conjunction with ESLint +**אמ;לק:** [ESLint](https://eslint.org) הוא הסטנדרט דה-פקטו למציאת שגיאות בקוד ותיקון של סגנונות קוד, לא רק זיהוי של רווח סורר שעלול ליצור תקלה אלא גם זיהוי של קוד שלא עומד בסטנדרטים (anti-pattern) כמו זריקת שגיאות ללא סיווג. אמנם ESLint יכול לתקן באופן אוטומטי סגנונות קוד, אך כלים אחרים כדוגמת [prettier](https://www.npmjs.com/package/prettier) טובים יותר בעיצוב וסגנון הקוד ועובדים בשילוב עם ESLint. -**אחרת:** Developers will focus on tedious spacing and line-width concerns and time might be wasted overthinking the project's code style +**אחרת:** מפתחים ישתעממו תוך כדי השקעת זמנם במציאת רווחים סוררים וידאגו לאורך השורה והזמן היקר שלהם יבוזבז על איך לשמור על סגנון הקוד של הפרוייקט. -🔗 [**Read More: Using ESLint and Prettier**](./sections/codestylepractices/eslint_prettier.md) +🔗 [**לקריאה נוספת: שימוש ב-ESLint ו-Prettier**](./sections/codestylepractices/eslint_prettier.md)

    From 36e68f23b24e5dfa2f48ca3a654516ce796dba86 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 14:16:56 +0300 Subject: [PATCH 1740/1795] translatesection 3.1 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index dc3276d73..fd05b671f 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -90,7 +90,7 @@   [3.1 השתמשו ב-ESLint `#strategic`](#-31-use-eslint)
    -  [3.2 Use Node.js eslint extension plugins `#updated`](#-32-use-nodejs-eslint-extension-plugins)
    +  [3.2 השתמשו בתוספים של Node.js שמרחיבים את ESLint `#updated`](#-32-use-nodejs-eslint-extension-plugins)
      [3.3 Start a Codeblock's Curly Braces on the Same Line](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
      [3.5 Name your functions](#-35-name-your-functions)
    @@ -462,11 +462,11 @@ async function promisifyFunction() {

    -## ![✔] 3.2 Use Node.js eslint extension plugins +## ![✔] 3.2 השתמשו בתוספים של Node.js שמרחיבים את ESLint -**אמ;לק:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules +**אמ;לק:** על גבי הסטנדרט של חוקי ESLint שמכסים את שפת JavaScript, הוסיפו את התוספים היעודיים של Node.js כמו [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha), [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) ועוד תוספים שמממשים חוקים נוספים ומועילים. -**אחרת:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early +**אחרת:** הרבה תבניות קוד לא תקינות שבשימוש ב-Node.js נעלמות מתחת לרדאד. לדוגמה, מפתחים יכתבו `require(variableAsPath)` עם משתנה שמאפשר גישה לתיקיה בקוד, דבר שמאפשר לתוקפים להריץ כל קוד JS. אם תשתמשו בחוקי Node.js תוכלו לזהות את הטעות הזאת ולקבל עליה התראה מבעוד מועד.

    From 41ddb0a41f2bd3008a43aedeaa6c48025c5cf979 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 14:22:46 +0300 Subject: [PATCH 1741/1795] translate section 3.3 --- README.hebrew.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index fd05b671f..11948da8c 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -470,11 +470,11 @@ async function promisifyFunction() {

    -## ![✔] 3.3 Start a Codeblock's Curly Braces on the Same Line +## ![✔] 3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה -**אמ;לק:** The opening curly braces of a code block should be on the same line as the opening statement +**אמ;לק:** הסוגריים המסולסלים הפותחים של בלוק של קוד חייבים להיות באותה השורה יחד עם הקוד. -### Code Example +### קוד דוגמה ```javascript // Do @@ -483,14 +483,15 @@ function someFunction() { } // Avoid -function someFunction() { +function someFunction() +{ // code block } ``` -**אחרת:** Deferring from this best practice might lead to unexpected results, as seen in the StackOverflow thread below: +**אחרת:** התעלמות משיטת עבודה זאת עלולה להוביל לתוצאות לא צפויות, כמו שניתן לראות בשרשור בקישור מ StackOverflow: -🔗 [**Read more:** "Why do results vary based on curly brace placement?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) +🔗 [**לקריאה נוספת:** "למה התוצאות משתנות בהתאם למיקום הסוגר המסולסל?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement)

    From 86644fafe7ac86e26292cf2cf09a9f76d9396b0b Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 14:24:11 +0300 Subject: [PATCH 1742/1795] translate section 3.3 fixes --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 11948da8c..adc6a7428 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -91,7 +91,7 @@   [3.1 השתמשו ב-ESLint `#strategic`](#-31-use-eslint)
      [3.2 השתמשו בתוספים של Node.js שמרחיבים את ESLint `#updated`](#-32-use-nodejs-eslint-extension-plugins)
    -  [3.3 Start a Codeblock's Curly Braces on the Same Line](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
    +  [3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
      [3.5 Name your functions](#-35-name-your-functions)
      [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
    @@ -472,9 +472,9 @@ async function promisifyFunction() { ## ![✔] 3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה -**אמ;לק:** הסוגריים המסולסלים הפותחים של בלוק של קוד חייבים להיות באותה השורה יחד עם הקוד. +**אמ;לק:** מומלץ שהסוגריים המסולסלים הפותחים של בלוק של קוד יהיו באותה השורה יחד עם הקוד. -### קוד דוגמה +### דוגמה ```javascript // Do @@ -489,7 +489,7 @@ function someFunction() } ``` -**אחרת:** התעלמות משיטת עבודה זאת עלולה להוביל לתוצאות לא צפויות, כמו שניתן לראות בשרשור בקישור מ StackOverflow: +**אחרת:** התעלמות משיטת עבודה זו עלולה להוביל לתוצאות לא צפויות, כמו שניתן לראות בשרשור בקישור מ StackOverflow: 🔗 [**לקריאה נוספת:** "למה התוצאות משתנות בהתאם למיקום הסוגר המסולסל?" (StackOverflow)](https://stackoverflow.com/questions/3641519/why-does-a-results-vary-based-on-curly-brace-placement) From 1cebe2826ee203b96c7c85690acf234426f17e26 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 14:41:32 +0300 Subject: [PATCH 1743/1795] translate section 3.4 --- README.hebrew.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index adc6a7428..6d57ed1be 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -92,7 +92,7 @@   [3.1 השתמשו ב-ESLint `#strategic`](#-31-use-eslint)
      [3.2 השתמשו בתוספים של Node.js שמרחיבים את ESLint `#updated`](#-32-use-nodejs-eslint-extension-plugins)
      [3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
    -  [3.4 Separate your statements properly](#-34-separate-your-statements-properly)
    +  [3.4 הפרידו בין ההצהרות השונות בצורה תקנית](#-34-separate-your-statements-properly)
      [3.5 Name your functions](#-35-name-your-functions)
      [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
    @@ -495,15 +495,15 @@ function someFunction()

    -## ![✔] 3.4 Separate your statements properly +## ![✔] 3.4 הפרידו בין ההצהרות השונות בצורה תקנית -No matter if you use semicolons or not to separate your statements, knowing the common pitfalls of improper linebreaks or automatic semicolon insertion, will help you to eliminate regular syntax errors. +בין אם אתם משתמשים בנקודה-פסיק (;) בשביל להפריד בין ההצהרות על המשתנים ובין אם לא, עצם הידיעה על ההשלכות של ירידת שורה במקום הלא מתאים או של הוספה אוטומטית של נקודה-פסיק, יעזרו לכם לזהות שגיאות סינטקס רגילות. -**אמ;לק:** Use ESLint to gain awareness about separation concerns. [Prettier](https://prettier.io/) or [Standardjs](https://standardjs.com/) can automatically resolve these issues. +**אמ;לק:** שימוש ב-ESLint כדי להעלות את המודעות לגבי הסיכון הכרוך בזה. כלים כמו [Prettier](https://prettier.io/) או [Standardjs](https://standardjs.com/) יכולים באופן אוטומטי לפתור את הבעיות הללו. -**אחרת:** As seen in the previous section, JavaScript's interpreter automatically adds a semicolon at the end of a statement if there isn't one, or considers a statement as not ended where it should, which might lead to some undesired results. You can use assignments and avoid using immediately invoked function expressions to prevent most of the unexpected errors. +**אחרת:** כמו שראינו בסעיף הקודם, "המתורגמן" (interpreter) של JavaScript מוסיף אוטומטית נקודה-פסיק בסוף כל הצהרה במידה ואין, או שהוא מחליט כי ההצהרה מסתיימת במקום אחר מהמתוכנן על ידינו, דבר שעלול להוביל לתוצאות בלתי צפויות. אפשר להשתמש בהשמות ולהימנע מ [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) כדי להימנע מרוב ההתנהגויות הבלתי צפויות. -### Code example +### דוגמה ```javascript // Do @@ -534,8 +534,9 @@ const count = 2 // it tries to run 2(), but 2 is not a function // put a semicolon before the immediate invoked function, after the const definition, save the return value of the anonymous function to a variable or avoid IIFEs altogether ``` -🔗 [**Read more:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) -🔗 [**Read more:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline) +🔗 [**לקריאה נוספת:** "Semi ESLint rule"](https://eslint.org/docs/rules/semi) +
    +🔗 [**לקריאה נוספת:** "No unexpected multiline ESLint rule"](https://eslint.org/docs/rules/no-unexpected-multiline)

    From ae00fa0013208788027918e47803f7eb8a5f4a0e Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 23:14:00 +0300 Subject: [PATCH 1744/1795] translate section 3.5 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 6d57ed1be..275a55fea 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -93,7 +93,7 @@   [3.2 השתמשו בתוספים של Node.js שמרחיבים את ESLint `#updated`](#-32-use-nodejs-eslint-extension-plugins)
      [3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 הפרידו בין ההצהרות השונות בצורה תקנית](#-34-separate-your-statements-properly)
    -  [3.5 Name your functions](#-35-name-your-functions)
    +  [3.5 תנו לפונקציה שם](#-35-name-your-functions)
      [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
    @@ -540,11 +540,11 @@ const count = 2 // it tries to run 2(), but 2 is not a function

    -## ![✔] 3.5 Name your functions +## ![✔] 3.5 תנו לפונקציה שם -**אמ;לק:** Name all functions, including closures and callbacks. Avoid anonymous functions. This is especially useful when profiling a node app. Naming all functions will allow you to easily understand what you're looking at when checking a memory snapshot +**אמ;לק:** תנו שמות לכל הפונקציות, כולל closures ו-callbacks. הימנעו מפונקציות אנונימיות. זה מאוד שימושי כשבודקים אפליקציות Node.js. מתן שמות לכל הפונקציות יאפשר לכם להבין בקלות על מה אתם מסתכלים כשאתם צופים בתמונת מצב של הזיכרון של האפליקציה. -**אחרת:** Debugging production issues using a core dump (memory snapshot) might become challenging as you notice significant memory consumption from anonymous functions +**אחרת:** לדבג את גרסת היצור (production) על בסיס תמונת מצב של הזיכרון (core dump) עלול להיות מאתגר כשהבעיות של הזיכרון קורות בכל מיני פונקציות אנונימיות.

    From 7e612ff15332bf9716541c333bbecf84de8092cd Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 23:24:07 +0300 Subject: [PATCH 1745/1795] translate section 3.6 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 275a55fea..d08dc5848 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -94,7 +94,7 @@   [3.3 התחילו בלוק של קוד עם סוגריים מסולסלים באותה השורה](#-33-start-a-codeblocks-curly-braces-on-the-same-line)
      [3.4 הפרידו בין ההצהרות השונות בצורה תקנית](#-34-separate-your-statements-properly)
      [3.5 תנו לפונקציה שם](#-35-name-your-functions)
    -  [3.6 Use naming conventions for variables, constants, functions and classes](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
    +  [3.6 השתמשו במוסכמות קבועות במתן שמות למשתנים, לקבועים, לפונקציות ולמחלקות](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
      [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
    @@ -548,13 +548,13 @@ const count = 2 // it tries to run 2(), but 2 is not a function

    -## ![✔] 3.6 Use naming conventions for variables, constants, functions and classes +## ![✔] 3.6 השתמשו במוסכמות קבועות במתן שמות למשתנים, לקבועים, לפונקציות ולמחלקות -**אמ;לק:** Use **_lowerCamelCase_** when naming constants, variables and functions, **_UpperCamelCase_** (capital first letter as well) when naming classes and **_UPPER_SNAKE_CASE_** when naming global or static variables. This will help you to easily distinguish between plain variables, functions, classes that require instantiation and variables declared at global module scope. Use descriptive names, but try to keep them short +**אמ;לק:** השתמשו ב-**_lowerCamelCase_** כאשר אתם נותנים שמות לקבועים, משתנים ופונקציות, **_UpperCamelCase_** (גם האות הראשונה גדולה) כאשר אתם נותנים שמות למחלקות ו-**_UPPER_SNAKE_CASE_** כאשר אתם נותנים שמות למשתנים גלובליים או סטטיים. סדר זה יאפשר לכם להבחין בקלות בין משתנים רגילים ופונקציות לבין מחלקות שדורשות אתחול ולבין משתנים גלובליים. השתמשו בשמות שמתארים היטב את משמעות המשתנה, אך שיהיה קצר. -**אחרת:** JavaScript is the only language in the world that allows invoking a constructor ("Class") directly without instantiating it first. Consequently, Classes and function-constructors are differentiated by starting with UpperCamelCase +**אחרת:** JavaScript היא השפה היחידה בעולם שתאפשר לכם לקרוא ל-constructor ("Class") ישירות ללא אתחול. לכן, חשוב מאוד להבדיל בין שמות מחלקות ושמות פונקציות על ידי שימוש ב-UpperCamelCase. -### 3.6 Code Example +### דוגמאות ```javascript // for global variables names we use the const/let keyword and UPPER_SNAKE_CASE From f4ff5ddf1fa7aeccacebafdd905782504a0e857f Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 23:36:24 +0300 Subject: [PATCH 1746/1795] translate section 3.7 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index d08dc5848..3aa0b7262 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -95,7 +95,7 @@   [3.4 הפרידו בין ההצהרות השונות בצורה תקנית](#-34-separate-your-statements-properly)
      [3.5 תנו לפונקציה שם](#-35-name-your-functions)
      [3.6 השתמשו במוסכמות קבועות במתן שמות למשתנים, לקבועים, לפונקציות ולמחלקות](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
    -  [3.7 Prefer const over let. Ditch the var](#-37-prefer-const-over-let-ditch-the-var)
    +  [3.7 העדיפו const על פני let. ניטשו את var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
      [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 Use the === operator](#-310-use-the--operator)
    @@ -589,13 +589,13 @@ function doSomething() {

    -## ![✔] 3.7 Prefer const over let. Ditch the var +## ![✔] 3.7 העדיפו const על פני let. ניטשו את var -**אמ;לק:** Using `const` means that once a variable is assigned, it cannot be reassigned. Preferring `const` will help you to not be tempted to use the same variable for different uses, and make your code clearer. If a variable needs to be reassigned, in a for loop, for example, use `let` to declare it. Another important aspect of `let` is that a variable declared using it is only available in the block scope in which it was defined. `var` is function scoped, not block-scoped, and [shouldn't be used in ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) now that you have `const` and `let` at your disposal +**אמ;לק:** שימוש ב-`const` משמעותו היא שלאחר שהמשתנה מאותחל לראשונה הוא לא יכול להיות מאותחל שוב. העדפת שימוש ב-`const` תעזור לכם לא להתפתות ולהשתמש שוב באותו משתנה לצרכים שונים ותהפוך את הקוד שלכם לקריא יותר. אם משתנה צריך להיות מאותחל מחדש, למשל בתוך לולאת for, אז השתמשו ב-`let` לצורך כך. נקודה נוספת שחשוב לציין היא ששימוש ב-`let` אפשרית רק בתוך אותו הבלוק שהיא הוגדרה בו. `var` נצמד לscope של הפונקציה שהוא מוגדר בו ולא לבלוק ספציפי ולכן [צריך לא להשתמש בו ב-ES6](https://hackernoon.com/why-you-shouldnt-use-var-anymore-f109a58b9b70) כשאפשר להשתמש ב-`const` וב-`let`. -**אחרת:** Debugging becomes way more cumbersome when following a variable that frequently changes +**אחרת:** דיבוג הופך להיות מאוד מסורבל כאשר משתנה משתנה לעיתים דחופות. -🔗 [**Read more: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75) +🔗 [**לקריאה נוספת: JavaScript ES6+: var, let, or const?** ](https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75)

    From b1029623311c1b91565fa9048c81075ce61d017a Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 16 Jul 2023 23:48:02 +0300 Subject: [PATCH 1747/1795] translate section 3.8 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 3aa0b7262..fe0bb6df6 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -96,7 +96,7 @@   [3.5 תנו לפונקציה שם](#-35-name-your-functions)
      [3.6 השתמשו במוסכמות קבועות במתן שמות למשתנים, לקבועים, לפונקציות ולמחלקות](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 העדיפו const על פני let. ניטשו את var](#-37-prefer-const-over-let-ditch-the-var)
    -  [3.8 Require modules first, not inside functions](#-38-require-modules-first-not-inside-functions)
    +  [3.8 טענו מודולים בתחילה, ולא בקריאה לפונקציות](#-38-require-modules-first-not-inside-functions)
      [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 Use the === operator](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
    @@ -599,11 +599,11 @@ function doSomething() {

    -## ![✔] 3.8 Require modules first, not inside functions +## ![✔] 3.8 טענו מודולים בתחילה, ולא בקריאה לפונקציות -**אמ;לק:** Require modules at the beginning of each file, before and outside of any functions. This simple best practice will not only help you easily and quickly tell the dependencies of a file right at the top but also avoids a couple of potential problems +**אמ;לק:** טענו את המודולים (require...) בתחילת כל קובץ, לפני כל הפונקציות. שיטת עבודה פשוטה זו לא רק שתעזור לכם בקלות ובמהירות לזהות את התלויות של קובץ מסוים, אלא גם תמנע מספר בעיות אפשריות. -**אחרת:** Requires are run synchronously by Node.js. If they are called from within a function, it may block other requests from being handled at a more critical time. Also, if a required module or any of its dependencies throw an error and crash the server, it is best to find out about it as soon as possible, which might not be the case if that module is required from within a function +**אחרת:** טעינת מודולים היא תהליך סינכרוני ב-Node.js. אם הטעינה תתבצע מתוך פונקציה היא עלולה לחסום טיפול בבקשות אחרות בזמן קריטי. בנוסף לכך, אם מודול חיוני או מישהו שהוא תלוי בו יזרקו שגיאה ויפילו את השרת, מומלץ שזה יוודע כמה שיותר מוקדם, מה שלא בטוח יקרה במקרה שהמודול נטען מתוך פונקציה.

    From 3751b89eb9e451dac80a5a473e1ec0903d1a375e Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 17 Jul 2023 21:33:54 +0300 Subject: [PATCH 1748/1795] translate section 3.9 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index fe0bb6df6..06aadbf5c 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -97,7 +97,7 @@   [3.6 השתמשו במוסכמות קבועות במתן שמות למשתנים, לקבועים, לפונקציות ולמחלקות](#-36-use-naming-conventions-for-variables-constants-functions-and-classes)
      [3.7 העדיפו const על פני let. ניטשו את var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 טענו מודולים בתחילה, ולא בקריאה לפונקציות](#-38-require-modules-first-not-inside-functions)
    -  [3.9 Set an explicit entry point to a module/folder `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
    +  [3.9 הגדירו כניסה מסודרת לספריה שלכם `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 Use the === operator](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    @@ -607,13 +607,13 @@ function doSomething() {

    -## ![✔] 3.9 Set an explicit entry point to a module/folder +## ![✔] 3.9 הגדירו כניסה מסודרת לספריה שלכם -**אמ;לק:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality +**אמ;לק:** בעת פיתוח מודול או ספריה, הגדירו קובץ בסיס שמייצא את הקוד המיועד לשימוש חיצוני. מנעו מהמשתמשים של הקוד שלכם את הצורך לייבא קבצים שיושבים עמוק אצלכם ואת הצורך שלהם להבין את מבנה הקבצים שלכם. כאשר עובדים בשיטת commonjs (require), זה יכול להיעשות על ידי שימוש בקובץ index.js שיושב בתיקיה הראשית או בהגדרת השדה main בקובץ package.json. כאשר עובדים בשיטת ESM (import), אם קובץ package.json קיים בתיקיה הראשית, אז השדה "exports" מאפשר את הגדרת הקובץ הראשי. אך אם אין קובץ package.json, אז שימוש בקובץ index.js בתיקיה הראשית ייצא את כל הפונקציונליות שמיועדת לשימוש חיצוני. -**אחרת:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract +**אחרת:** קיומו של קובץ ראשי רשמי משמש כממשק חיצוני שמסתיר את החלקים הפנימיים של הספריה, מקשר את המשתמש ישירות לקוד הזמין ומאפשר שינויים עתידיים ללא צורך לשבוראת החוזה. -### 3.9 Code example - avoid coupling the client to the module structure +### דוגמה ```javascript // Avoid: client has deep familiarity with the internals From fd3df970df19530c5df96e6b62c366515f5ee737 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 17 Jul 2023 21:40:25 +0300 Subject: [PATCH 1749/1795] translate section 3.10 --- README.hebrew.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 06aadbf5c..66a968999 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -98,7 +98,7 @@   [3.7 העדיפו const על פני let. ניטשו את var](#-37-prefer-const-over-let-ditch-the-var)
      [3.8 טענו מודולים בתחילה, ולא בקריאה לפונקציות](#-38-require-modules-first-not-inside-functions)
      [3.9 הגדירו כניסה מסודרת לספריה שלכם `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
    -  [3.10 Use the === operator](#-310-use-the--operator)
    +  [3.10 השתמשו באופרטור `===`](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
      [3.13 Avoid effects outside of functions `#new`](#-313-avoid-effects-outside-of-functions)
    @@ -632,13 +632,13 @@ const { SMSWithMedia } = require("./SMSProvider");

    -## ![✔] 3.10 Use the `===` operator +## ![✔] 3.10 השתמשו באופרטור `===` -**אמ;לק:** Prefer the strict equality operator `===` over the weaker abstract equality operator `==`. `==` will compare two variables after converting them to a common type. There is no type conversion in `===`, and both variables must be of the same type to be equal +**אמ;לק:** העדיפו את ההשוואה הקפדנית באמצעות האופרטור `===` על פני ההשוואה החלשה יותר באמצעות האופרטור `==`. `==` משווה שני משתנים אחרי המרה של שניהם לסוג משתנה אחד. אין המרת סוגי משתנים באופרטור `===`, ושני המשתנים חייבים להיות מאותו סוג כדי שיוכלו להיות שווים. -**אחרת:** Unequal variables might return true when compared with the `==` operator +**אחרת:** משתנים בעלי ערכים שונים עלולים להחזיר `true` כאשר משווים ביניהם בעזרת האופרטור `==`. -### 3.10 Code example +### דוגמאות ```javascript "" == "0"; // false @@ -655,7 +655,7 @@ null == undefined; // true " \t\r\n " == 0; // true ``` -All statements above will return false if used with `===` +כל ההשוואות לעיל יחזירו `false` בעת השוואה עם `===`.

    From 763220e89d5821cd90043d5f6de3d6564bb211a7 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 17 Jul 2023 21:58:20 +0300 Subject: [PATCH 1750/1795] translate section 3.12 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 66a968999..7c5e225d1 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -100,7 +100,7 @@   [3.9 הגדירו כניסה מסודרת לספריה שלכם `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 השתמשו באופרטור `===`](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
    -  [3.12 Use arrow function expressions (=>)](#-312-use-arrow-function-expressions-)
    +  [3.12 השתמשו בפונקציות חץ (=>)](#-312-use-arrow-function-expressions-)
      [3.13 Avoid effects outside of functions `#new`](#-313-avoid-effects-outside-of-functions)
    @@ -669,13 +669,13 @@ null == undefined; // true

    -## ![✔] 3.12 Use arrow function expressions (=>) +## ![✔] 3.12 השתמשו בפונקציות חץ (=>) -**אמ;לק:** Though it's recommended to use async-await and avoid function parameters when dealing with older APIs that accept promises or callbacks - arrow functions make the code structure more compact and keep the lexical context of the root function (i.e. `this`) +**אמ;לק:** אמנם מומלץ להשתמש ב async-await ולהימנע מהגדרת פרמטרים בפונקציות כאשר מתעסקים עם API ישן שתומך ב-callbacks או הבטחות - פונקציות חץ מאפשרות לארגן את הקוד קומפקטי יותר וכמובן ששומרות על הקונטקסט של פונקצית המעטפת (`this`). -**אחרת:** Longer code (in ES5 functions) is more prone to bugs and cumbersome to read +**אחרת:** קוד ארוך יותר (על בסיס פונקציות של ES5) חשוף ליותר באגים וקשה יותר לקריאה. -🔗 [**Read more: It’s Time to Embrace Arrow Functions**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75) +🔗 [**לקריאה נוספת: הגיע הזמן לאמץ את פונקציות החץ**](https://medium.com/javascript-scene/familiarity-bias-is-holding-you-back-its-time-to-embrace-arrow-functions-3d37e1a9bb75)

    From ad9a4ea69ff5836a6d80dcb78bfbd575d9f3f5a4 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 17 Jul 2023 22:11:10 +0300 Subject: [PATCH 1751/1795] translate section 3.13 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 7c5e225d1..4d292b034 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -86,7 +86,7 @@
    - 3. תבניות קוד וסגנון עיצוב (12) + 3. תבניות קוד וסגנון עיצוב (13)   [3.1 השתמשו ב-ESLint `#strategic`](#-31-use-eslint)
    @@ -101,7 +101,7 @@   [3.10 השתמשו באופרטור `===`](#-310-use-the--operator)
      [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 השתמשו בפונקציות חץ (=>)](#-312-use-arrow-function-expressions-)
    -  [3.13 Avoid effects outside of functions `#new`](#-313-avoid-effects-outside-of-functions)
    +  [3.13 הימנעו מהשפעות צדדיות מחוץ לפונקציות `#new`](#-313-avoid-effects-outside-of-functions)
    @@ -679,11 +679,11 @@ null == undefined; // true

    -## ![✔] 3.13 Avoid effects outside of functions +## ![✔] 3.13 הימנעו מהשפעות צדדיות מחוץ לפונקציות -**אמ;לק:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns +**אמ;לק:** הימנעו מכתיבת קוד עם השפעות צדדיות כמו פעולת רשת או פניה למסד נתונים מחוץ לפונקציה. אם כן תכתבו קוד כזה הוא ירוץ מיד כאשר קובץ אחר פונה לקובץ הזה. הקוד 'הצף' הזה עלול לרוץ כאשר התשתית אותה הוא מבקש עוד לא זמינה עבורו. זה גם פוגע בביצועים אפילו אם אין צורך בפונקציה שעבורה מתבצעת הפעולה בזמן הריצה. דבר אחרון, כתיבת כיסוי לפעולה זו בשביל בדיקות הרבה יותר מורכבת כשהיא לא נעשית בפונקציה. במקום זאת, שימו את הקוד הזה בפונקציה שצריכה להיקרא במפורש. אם הקוד הזה צריך להיקרא ישר בעת עליית המערכת, שיקלו שימוש ב-factory או בתבנית אחרת שמתאימה לדרישה כזאת. -**אחרת:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data +**אחרת:** תשתיות סטנדרטיות בעולם הווב מגדירות ניהול שגיאות, משתני סביבה וניטור תקלות. אם הפעולה תתבצע לפני שהתשתית מאותחלת אז לא יהיה ניטור של המקרה או שהפעולה תיכשל בשל חוסר בהגדרות שטרם נטענו.


    From b6d1a8ba7b004e45b2966eac630618b21cc49569 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 10:42:31 +0300 Subject: [PATCH 1752/1795] translate section 4 intro --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 4d292b034..652f544a8 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -691,11 +691,11 @@ null == undefined; // true # `4. בדיקות ובקרת איכות` -\_We have dedicated guides for testing, see below. The best practices list here is a brief summary of these guides +> יש לנו מדריכים יעודיים לכתיבת בדיקות. רשימת שיטות העבודה המומלצות פה היא סיכום כללי של המדריכים הללו. +> +> א. [שיטות עבודה מומלצות בכתיבת בדיקות ל-JavaScript](https://github.com/goldbergyoni/javascript-testing-best-practices)
    +> ב. [בדיקות ב-Node.js - אחרי היסודות](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -a. [JavaScript testing best practices](https://github.com/goldbergyoni/javascript-testing-best-practices) -b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -\_ ## ![✔] 4.1 At the very least, write API (component) testing From 584658b782fe1f52f45669f9bba6d0a0e4d52cef Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 10:52:00 +0300 Subject: [PATCH 1753/1795] translate section 4.1 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 652f544a8..274f032e8 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -110,7 +110,7 @@ 4. בדיקות ובקרת איכות (13) -  [4.1 At the very least, write API (component) testing `#strategic`](#-41-at-the-very-least-write-api-component-testing)
    +  [4.1 לפחות, כיתבו בדיקות API לרכיבים השונים `#strategic`](#-41-at-the-very-least-write-api-component-testing)
      [4.2 Include 3 parts in each test name `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
      [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
    @@ -697,11 +697,11 @@ null == undefined; // true > ב. [בדיקות ב-Node.js - אחרי היסודות](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -## ![✔] 4.1 At the very least, write API (component) testing +## ![✔] 4.1 לפחות, כיתבו בדיקות API לרכיבים השונים -**אמ;לק:** Most projects just don't have any automated testing due to short timetables or often the 'testing project' ran out of control and was abandoned. For that reason, prioritize and start with API testing which is the easiest way to write and provides more coverage than unit testing (you may even craft API tests without code using tools like [Postman](https://www.getpostman.com/)). Afterwards, should you have more resources and time, continue with advanced test types like unit testing, DB testing, performance testing, etc +**אמ;לק:** ברוב הפרויקטים אין בדיקות אוטומטיות כלל בשל לוח זמנים קצר, או שהתחילו לנסות להוסיף בדיקות בפרויקט נוסף אך זה יצא משליטה וננטש עם הזמן. לכן, לתעדף ולהתחיל בדיקות API שזאת הדרך הקלה לכתוב בדיקות ולספק כיסוי (בדיקות) של הקוד מאשר בבדיקות יחידה של פונקציות בודדות (אפשר להשתמש בשביל זה גם בכלים חיצוניים ללא כתיבת קוד, למשל שימוש ב-[Postman](https://www.getpostman.com/)). לאחר מכן, אם יש לכם יותר משאבים וזמן תמשיכו עם בדיקות מתקדמות יותר כגון בדיקות יחידה, בדיקות מול מסדי הנתונים בדיקות ביצועים ועוד. -**אחרת:** You may spend long days on writing unit tests to find out that you got only 20% system coverage +**אחרת:** אתם עלולים לבזבז ימים שלמים על כתיבת בדיקות יחידה בלבד ולגלות בסופו של דבר שכיסיתם רק 20% מהמערכת.

    From c6421fdabc51ee07dd607098fa3be47e5ac162e4 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 11:04:50 +0300 Subject: [PATCH 1754/1795] translate section 4.2 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 274f032e8..0d9cd2e9f 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -111,7 +111,7 @@   [4.1 לפחות, כיתבו בדיקות API לרכיבים השונים `#strategic`](#-41-at-the-very-least-write-api-component-testing)
    -  [4.2 Include 3 parts in each test name `#new`](#-42-include-3-parts-in-each-test-name)
    +  [4.2 סווגו 3 חלקים במתן שם לכל בדיקה `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
      [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
    @@ -705,13 +705,13 @@ null == undefined; // true

    -## ![✔] 4.2 Include 3 parts in each test name +## ![✔] 4.2 סווגו 3 חלקים במתן שם לכל בדיקה -**אמ;לק:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result +**אמ;לק:** גירמו לבדיקה לתאר את שלב הדרישות כך שהיא תסביר את עצמה גם לQA או לאחרים (כולל אתכם בעתיד הלא רחוק) שלא בקיאים בחלקים הפנימיים של הקוד. ציינו בבדיקה (1) איזה חלק נבדק, (2) באילו תנאים (3) ומה התוצאה שמצפים שתחול. -**אחרת:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? +**אחרת:** ההתקנה בדיוק נכשלה, בדיקה בשם “Add product” נכשלה. האם זה מתאר מה בדיוק לא תיפקד? -🔗 [**Read More: Include 3 parts in each test name**](./sections/testingandquality/3-parts-in-name.md) +🔗 [**לקריאה נוספת: סווגו 3 חלקים במתן שם לכל בדיקה**](./sections/testingandquality/3-parts-in-name.md)

    From 8e7210d8c16a50ffbd122c115a7f1d0be50bc09c Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 12:36:58 +0300 Subject: [PATCH 1755/1795] translate section 4.3 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 0d9cd2e9f..547b88145 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -112,7 +112,7 @@   [4.1 לפחות, כיתבו בדיקות API לרכיבים השונים `#strategic`](#-41-at-the-very-least-write-api-component-testing)
      [4.2 סווגו 3 חלקים במתן שם לכל בדיקה `#new`](#-42-include-3-parts-in-each-test-name)
    -  [4.3 Structure tests by the AAA pattern `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
    +  [4.3 חלקו את הבדיקות לפי תבנית ה-AAA `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
      [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
    @@ -715,13 +715,13 @@ null == undefined; // true

    -## ![✔] 4.3 Structure tests by the AAA pattern +## ![✔] 4.3 חלקו את הבדיקות לפי תבנית ה-AAA -**אמ;לק:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan +**אמ;לק:** חלקו את הבדיקות לשלושה חלקים נפרדים: Arrange (ארגן), Act (פעל) & Assert (ודא) (AAA). החלק הראשון כולל את ההכנה של הסביבה לבדיקה, החלק השני את ההרצה במצב בדיקות, ולבסוף החלק שמוודא שהתקבלה התוצאה הרצויה. שימוש במבנה זה בעקביות מבטיח שהקורא לא יבזבז זמן מחשבה של הבנת הבדיקה. -**אחרת:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain +**אחרת:** לא מספיק שיתבזבז זמן נרחב מהיום על הבנת הקוד, עכשיו גם החלק הקל ביום (הבנת הבדיקות) ישרוף את המוח. -🔗 [**Read More: Structure tests by the AAA pattern**](./sections/testingandquality/aaa.md) +🔗 [**לקריאה נוספת: חלקו את הבדיקות לפי תבנית ה-AAA**](./sections/testingandquality/aaa.md)

    From 774e269d153a68aa0aa07f45dd8de2b289047dc5 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 12:50:01 +0300 Subject: [PATCH 1756/1795] translate section 4.4 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 547b88145..364040834 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -113,7 +113,7 @@   [4.1 לפחות, כיתבו בדיקות API לרכיבים השונים `#strategic`](#-41-at-the-very-least-write-api-component-testing)
      [4.2 סווגו 3 חלקים במתן שם לכל בדיקה `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 חלקו את הבדיקות לפי תבנית ה-AAA `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
    -  [4.4 Ensure Node version is unified `#new`](#-44-ensure-node-version-is-unified)
    +  [4.4 וודאו כי גרסת ה-Node אחידה `#new`](#-44-ensure-node-version-is-unified)
      [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
      [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
    @@ -725,11 +725,11 @@ null == undefined; // true

    -## ![✔] 4.4 Ensure Node version is unified +## ![✔] 4.4 וודאו כי גרסת ה-Node אחידה -**אמ;לק:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) +**אמ;לק:** השתמשו בכלים המעודדים או אוכפים שימוש באותה גרסת Node.js בסביבות השונות ועל ידי שאר המפתחים. כלים כמו [nvm](https://github.com/nvm-sh/nvm), ו-[Volta](https://volta.sh/) מאפשרים להגדיר במפורש את הגרסה הנדרשת בפרויקט בקובץ כך שכל חברי הצוות יכולים על ידי הרצת פקודה אחת ליישר קו עם גרסת הפרויקט. ישנה אפשרות שגרסה זו גם תשתקף לתהליך ה-CI וסביבת היצור/לקוחות (לדוגמה על ידי העתקת מספר הגרסה המבוקש ל-`.Dockerfile` ולקבצי ההגדרות של תהליך ה-CI). -**אחרת:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed +**אחרת:** מפתחת עלולה להיתקל או לפספס שגיאה מכיוון שהיא משתמשת בגרסת Node.js שונה משאר הצוות. או גרוע מכך, סביבת היצור רצה באמצעות גרסה שונה מזו שהורצו עליה הבדיקות.

    From becd7fb4e6c1ed99edae6da280dbe8858de66377 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 14:04:09 +0300 Subject: [PATCH 1757/1795] translate section 4.5 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 364040834..f9e4091cc 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -114,7 +114,7 @@   [4.2 סווגו 3 חלקים במתן שם לכל בדיקה `#new`](#-42-include-3-parts-in-each-test-name)
      [4.3 חלקו את הבדיקות לפי תבנית ה-AAA `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
      [4.4 וודאו כי גרסת ה-Node אחידה `#new`](#-44-ensure-node-version-is-unified)
    -  [4.5 Avoid global test fixtures and seeds, add data per-test `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
    +  [4.5 הימנעו מאתחול מידע גרעיני משותף, הגדירו לפי צורך של בדיקה `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
      [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
    @@ -733,13 +733,13 @@ null == undefined; // true

    -## ![✔] 4.5 Avoid global test fixtures and seeds, add data per-test +## ![✔] 4.5 הימנעו מאתחול מידע גרעיני משותף, הגדירו לפי צורך של בדיקה -**אמ;לק:** To prevent test coupling and easily reason about the test flow, each test should add and act on its own set of DB rows. Whenever a test needs to pull or assume the existence of some DB data - it must explicitly add that data and avoid mutating any other records +**אמ;לק:** כדי להימנע מצמידות ותלות בין בדיקות שונות וכדי שיהיה ברור יותר איך להסביר מה קורה בשלבים השונים של הבדיקה, ראוי שכל בדיקה תוסיף ותנהל את המידע העוטף שלה (למשל שורות בטבלה). במקרה ובדיקה צריכה לצרוך מידע מטבלה או להניח שהוא קיים שם - היא צריכה קודם לכן להוסיף את המידע במפורש ולהימנע משינוי מידע של בדיקה אחרת. -**אחרת:** Consider a scenario where deployment is aborted due to failing tests, team is now going to spend precious investigation time that ends in a sad conclusion: the system works well, the tests however interfere with each other and break the build +**אחרת:** תארו לכם מקרה בו הפצת גרסה נכשלה בשל שגיאה בבדיקות, הצוות משנס מותניים לחקור את הסיבה ומגיע אם התובנה העצובה שהמערכת עובדת תקין אבל הבדיקות דורסות מידע אחת לשניה ולכן נכשלו ועצרו את תהליך ההפצה. -🔗 [**Read More: Avoid global test fixtures**](./sections/testingandquality/avoid-global-test-fixture.md) +🔗 [**לקריאה נוספת: הימנעו מאתחול מידע גרעיני משותף**](./sections/testingandquality/avoid-global-test-fixture.md)

    From 3239ebbe8e93e79fda3c0f1ceed1db6b831f85d3 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 14:22:56 +0300 Subject: [PATCH 1758/1795] translate section 4.6 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index f9e4091cc..496c68869 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -115,7 +115,7 @@   [4.3 חלקו את הבדיקות לפי תבנית ה-AAA `#strategic`](#-43-structure-tests-by-the-aaa-pattern)
      [4.4 וודאו כי גרסת ה-Node אחידה `#new`](#-44-ensure-node-version-is-unified)
      [4.5 הימנעו מאתחול מידע גרעיני משותף, הגדירו לפי צורך של בדיקה `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
    -  [4.6 Tag your tests `#advanced`](#-46-tag-your-tests)
    +  [4.6 תייגו את הבדיקות `#advanced`](#-46-tag-your-tests)
      [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
      [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
    @@ -743,11 +743,11 @@ null == undefined; // true

    -## ![✔] 4.6 Tag your tests +## ![✔] 4.6 תייגו את הבדיקות -**אמ;לק:** Different tests must run on different scenarios: quick smoke, IO-less, tests should run when a developer saves or commits a file, full end-to-end tests usually run when a new pull request is submitted, etc. This can be achieved by tagging tests with keywords like #cold #api #sanity so you can grep with your testing harness and invoke the desired subset. For example, this is how you would invoke only the sanity test group with [Mocha](https://mochajs.org/): mocha --grep 'sanity' +**אמ;לק:** בדיקות שונות צריכות לרוץ בתרחישים שונים: בדיקות שפיות (quick smoke/sanity), IO-less, בדיקות בעת שמירת קובץ או commit, בדיקות מלאות מקצה לקצה (e2e) כאשר נפתח PR וכולי... התרחישים השונים יכולים להיות מוגדרים בעזרת תיוג בדיקות שונות עם מילות מפתח כמו #cold #api #sanity דבר המאפשר להגדיר קבוצת בדיקות בהתאם לצורך ולהריץ רק אותה. למשל, זאת השיטה להריץ רק את קבוצת בדיקות השפיות באמצעות [Mocha](https://mochajs.org/): `mocha --grep 'sanity'`. -**אחרת:** Running all the tests, including tests that perform dozens of DB queries, any time a developer makes a small change can be extremely slow and keeps developers away from running tests +**אחרת:** הרצה של כל הבדיקות כולל כאלו שמבצעות עשרות פניות למסד נתונים בכל פעם שמפתח עושה שינוי קטן יאט את קצב הפיתוח בצורה ניכרת ותמנע מצוות הפיתוח להריץ בדיקות.

    From 5810b57d6af620f0fe5c1d4072b894c4d9e6486b Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 18 Jul 2023 23:51:54 +0300 Subject: [PATCH 1759/1795] translate section 4.7 --- README.hebrew.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 496c68869..68b4e9e42 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -116,7 +116,7 @@   [4.4 וודאו כי גרסת ה-Node אחידה `#new`](#-44-ensure-node-version-is-unified)
      [4.5 הימנעו מאתחול מידע גרעיני משותף, הגדירו לפי צורך של בדיקה `#strategic`](#-45-avoid-global-test-fixtures-and-seeds-add-data-per-test)
      [4.6 תייגו את הבדיקות `#advanced`](#-46-tag-your-tests)
    -  [4.7 Check your test coverage, it helps to identify wrong test patterns](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
    +  [4.7 בידקו את רמת כיסוי הבדיקות שלכם, זה יעזור לזהות דפוסי בדיקות שגויים](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
      [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
      [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
    @@ -751,11 +751,11 @@ null == undefined; // true

    -## ![✔] 4.7 Check your test coverage, it helps to identify wrong test patterns +## ![✔] 4.7 בידקו את רמת כיסוי הבדיקות שלכם, זה יעזור לזהות דפוסי בדיקות שגויים -**אמ;לק:** Code coverage tools like [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) are great for 3 reasons: it comes for free (no effort is required to benefit this reports), it helps to identify a decrease in testing coverage, and last but not least it highlights testing mismatches: by looking at colored code coverage reports you may notice, for example, code areas that are never tested like catch clauses (meaning that tests only invoke the happy paths and not how the app behaves on errors). Set it to fail builds if the coverage falls under a certain threshold +**אמ;לק:** כלים לבדיקת כיסוי הקוד על ידי בדיקות כמו [Istanbul](https://github.com/istanbuljs/istanbuljs)/[NYC](https://github.com/istanbuljs/nyc) מצוינים בשל שלוש סיבות: הם בחינם (אין עלות לדו"חות שהם מספקים), הם עוזרים לזהות ירידה באחוזי הכיסוי, ואחרון חביב הם מדגישים מקרים של אי התאמה בבדיקות: על ידי צפייה בצבעים שהדוחות הללו מספקים אפשר לזהות למשל שיש קטעי קוד שלא נבדקים לעולם כמו הסתעפויות של `catch` (מה שאומר שיש בדיקות רק למסלול המצליח ולא למקרים של השגיאות). רצוי להגדיר את זה כך שזה יפיל את תהליכי יצירת הגרסאות במידה והכיסוי לא עובר סף מסוים. -**אחרת:** There won't be any automated metric telling you when a large portion of your code is not covered by testing +**אחרת:** לא יהיה שום אמצעי מדידה שידווח שקטעים נרחבים מהקוד לא נבדקים כלל.

    From a60e2784034d5520d7a9812f15a3a990dbe94ba0 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Jul 2023 14:38:13 +0300 Subject: [PATCH 1760/1795] Update README.md --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9666f9ec8..f2b0a5c9f 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [5.15. Set NODE_ENV=production](#-515-set-node_envproduction)
      [5.16. Design automated, atomic and zero-downtime deployments `#advanced`](#-516-design-automated-atomic-and-zero-downtime-deployments)
      [5.17. Use an LTS release of Node.js](#-517-use-an-lts-release-of-nodejs)
    -  [5.18. Log to stdout, avoid specifying log destination within the app](#-518-log-to-stdout-avoid-specifying-log-destination-within-the-app)
    +  [5.18. Log to stdout, avoid specifying log destination within the app `#updated`](#-518-log-to-stdout-avoid-specifying-log-destination-within-the-app)
      [5.19. Install your packages with npm ci `#new`](#-519-install-your-packages-with-npm-ci)
    @@ -227,6 +227,8 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin ## ![✔] 1.1 Structure your solution by business components +### `📝 #updated` + **TL;DR:** The root of a system should contain folders or repositories that represent reasonably sized business modules. Each component represents a product domain (i.e., bounded context), like 'user-component', 'order-component', etc. Each component has its own API, logic, and logical database. What is the significant merit? With an autonomous component, every change is performed over a granular and smaller scope - the mental overload, development friction, and deployment fear are much smaller and better. As a result, developers can move much faster. This does not necessarily demand physical separation and can be achieved using a Monorepo or with a multi-repo ```bash @@ -248,6 +250,8 @@ my-system ## ![✔] 1.2 Layer your components with 3-tiers, keep the web layer within its boundaries +### `📝 #updated` + **TL;DR:** Each component should contain 'layers' - a dedicated folder for common concerns: 'entry-point' where controller lives, 'domain' where the logic lives, and 'data-access'. The primary principle of the most popular architectures is to separate the technical concerns (e.g., HTTP, DB, etc) from the pure logic of the app so a developer can code more features without worrying about infrastructural concerns. Putting each concern in a dedicated folder, also known as the [3-Tier pattern](https://en.wikipedia.org/wiki/Multitier_architecture), is the _simplest_ way to meet this goal ```bash @@ -291,6 +295,8 @@ my-system ## ![✔] 1.4 Use environment aware, secure and hierarchical config +### `📝 #updated` + **TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others **Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state @@ -301,6 +307,8 @@ my-system ## ![✔] 1.5 Consider all the consequences when choosing the main framework +### `🌟 New item` + **TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) **Otherwise:** Due to the overwhelming amount of considerations, it's easy to make decisions based on partial information and compare apples with oranges. For example, it's believed that Fastify is a minimal web-server that should get compared with express only. In reality, it's a rich framework with many official plugins that cover many concerns @@ -309,6 +317,8 @@ my-system ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully +### `🌟 New item` + **TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises **Otherwise:** [Researches](https://earlbarr.com/publications/typestudy.pdf) show that using TypeScript can help in detecting ~20% bugs earlier. Without it, also the developer experience in the IDE is intolerable. On the flip side, 80% of other bugs were not discovered using types. Consequently, typed syntax is valuable but limited. Only efficient tests can discover the whole spectrum of bugs, including type-related bugs. It might also defeat its purpose: sophisticated code features are likely to increase the code complexity, which by itself increases both the amount of bugs and the average bug fix time @@ -333,6 +343,8 @@ my-system ## ![✔] 2.2 Extend the built-in Error object +### `📝 #updated` + **TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! @@ -343,6 +355,8 @@ my-system ## ![✔] 2.3 Distinguish catastrophic errors from operational errors +### `📝 #updated` + **TL;DR:** Operational errors (e.g. API received an invalid input) refer to known cases where the error impact is fully understood and can be handled thoughtfully. On the other hand, catastrophic error (also known as programmer errors) refers to unusual code failures that dictate to gracefully restart the application **Otherwise:** You may always restart the application when an error appears, but why let ~5000 online users down because of a minor, predicted, operational error? The opposite is also not ideal – keeping the application up when an unknown catastrophic issue (programmer error) occurred might lead to an unpredicted behavior. Differentiating the two allows acting tactfully and applying a balanced approach based on the given context @@ -383,6 +397,8 @@ my-system ## ![✔] 2.7 Use a mature logger to increase errors visibility +### `📝 #updated` + **TL;DR:** A robust logging tools like [Pino](https://github.com/pinojs/pino) or [Winston](https://github.com/winstonjs/winston) increases the errors visibility using features like log-levels, pretty print coloring and more. Console.log lacks these imperative features and should be avoided. The best in class logger allows attaching custom useful properties to log entries with minimized serialization performance penalty. Developers should write logs to `stdout` and let the infrastructure pipe the stream to the appropriate log aggregator **Otherwise:** Skimming through console.logs or manually through messy text file without querying tools or a decent log viewer might keep you busy at work until late @@ -393,6 +409,8 @@ my-system ## ![✔] 2.8 Test error flows using your favorite test framework +### `📝 #updated` + **TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) **Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling @@ -413,6 +431,8 @@ my-system ## ![✔] 2.10 Catch unhandled promise rejections +### `📝 #updated` + **TL;DR:** Any exception thrown within a promise will get swallowed and discarded unless a developer didn’t forget to explicitly handle it. Even if your code is subscribed to `process.uncaughtException`! Overcome this by registering to the event `process.unhandledRejection` **Otherwise:** Your errors will get swallowed and leave no trace. Nothing to worry about @@ -433,6 +453,8 @@ my-system ## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace +### `🌟 New item` + **TL;DR:** Always do `return await` when returning a promise to benefit full error stacktrace. If a function returns a promise, that function must be declared as `async` function and explicitly `await` the promise before returning it @@ -461,6 +483,8 @@ especially if the cause of the abnormal behavior is inside of the missing functi ## ![✔] 3.2 Use Node.js eslint extension plugins +### `📝 #updated` + **TL;DR:** On top of ESLint standard rules that cover vanilla JavaScript, add Node.js specific plugins like [eslint-plugin-node](https://www.npmjs.com/package/eslint-plugin-node), [eslint-plugin-mocha](https://www.npmjs.com/package/eslint-plugin-mocha) and [eslint-plugin-node-security](https://www.npmjs.com/package/eslint-plugin-security), [eslint-plugin-require](https://www.npmjs.com/package/eslint-plugin-require), [/eslint-plugin-jest](https://www.npmjs.com/package/eslint-plugin-jest) and other useful rules **Otherwise:** Many faulty Node.js code patterns might escape under the radar. For example, developers might require(variableAsPath) files with a variable given as a path which allows attackers to execute any JS script. Node.js linters can detect such patterns and complain early @@ -604,6 +628,8 @@ function doSomething() { ## ![✔] 3.9 Set an explicit entry point to a module/folder +### `📝 #updated` + **TL;DR:** When developing a module/library, set an explicit root file that exports the public and interesting code. Discourage the client code from importing deep files and becoming familiar with the internal structure. With commonjs (require), this can be done with an index.js file at the folder's root or the package.json.main field. With ESM (import), if a package.json exists on the root, the field "exports" allow specifying the module's root file. If no package.json exists, you may put an index.js file on the root which re-exports all the public functionality **Otherwise:** Having an explicit root file acts like a public 'interface' that encapsulates the internal, directs the caller to the public code and facilitates future changes without breaking the contract @@ -676,6 +702,8 @@ All statements above will return false if used with `===` ## ![✔] 3.13 Avoid effects outside of functions +### `🌟 New item` + **TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns **Otherwise:** A typical web framework sets error handler, environment variables and monitoring. When DB/network calls are made before the web framework is initialized, they won't be monitored or fail due to a lack of configuration data @@ -702,6 +730,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.2 Include 3 parts in each test name + ### `🌟 New item` + **TL;DR:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result **Otherwise:** A deployment just failed, a test named “Add product” failed. Does this tell you what exactly is malfunctioning? @@ -712,6 +742,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.3 Structure tests by the AAA pattern +### `🌟 New item` + **TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan **Otherwise:** Not only you spend long daily hours on understanding the main code, but now also what should have been the simple part of the day (testing) stretches your brain @@ -722,6 +754,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.4 Ensure Node version is unified +### `🌟 New item` + **TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) **Otherwise:** A developer might face or miss an error because she uses a different Node.js version than her teammates. Even worse - the production runtime might be different than the environment where tests were executed @@ -774,6 +808,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.10 Mock responses of external HTTP services +### `🌟 New item` + **TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production **Otherwise:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow @@ -790,6 +826,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.12 Specify a port in production, randomize in testing +### `🌟 New item` + **TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port **Otherwise:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default @@ -798,6 +836,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.13 Test the five possible outcomes +### `🌟 New item` + **TL;DR:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) **Otherwise:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) @@ -882,6 +922,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.8. Discover the unknowns using APM products +### `📝 #updated` + **TL;DR:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example **Otherwise:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX @@ -980,6 +1022,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.18. Log to stdout, avoid specifying log destination within the app +### `📝 #updated` + **TL;DR:** Log destinations should not be hard-coded by developers within the application code, but instead should be defined by the execution environment the application runs in. Developers should write logs to `stdout` using a logger utility and then let the execution environment (container, server, etc.) pipe the `stdout` stream to the appropriate destination (i.e. Splunk, Graylog, ElasticSearch, etc.). **Otherwise:** If developers set the log routing, less flexibility is left for the ops professional who wishes to customize it. Beyond this, if the app tries to log directly to a remote location (e.g., Elastic Search), in case of panic or crash - further logs that might explain the problem won't arrive @@ -1311,6 +1355,8 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 6.27. Import built-in modules using the 'node:' protocol +### `🌟 New item` + **TL;DR:** Import or require built-in Node.js modules using the 'node protocol' syntax: @@ -1494,6 +1540,8 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args +### `🌟 New item` + **TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces **Otherwise:** Everyone with access to the CI and docker registry will also get access to some precious organization secrets as a bonus @@ -1532,6 +1580,8 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.15. Lint your Dockerfile +### `🌟 New item` + **TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. **Otherwise:** Mistakenly the Dockerfile creator left Root as the production user, and also used an image from unknown source repository. This could be avoided with with just a simple linter. From 81e7a10b39026d8d2b7640c9d4b3bab0873aca36 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Jul 2023 14:56:38 +0300 Subject: [PATCH 1761/1795] Update README.md --- README.md | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f2b0a5c9f..f6879f41b 100644 --- a/README.md +++ b/README.md @@ -22,19 +22,14 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    -## 🚀 We have an [official Node.js starter - Practica.js](https://github.com/practicajs/practica). Use it to generate a new solution skeleton with all the practices baked inside. Or just it to learn by code examples +# 🎊 2023 edition is here! -
    - -# Latest Best Practices and News - -- **🛰 2023 edition is released soon**: We're now writing the next edition, stay tuned? +- **🛰 Modernized to 2023**: Tons of text edits, new recommended libraries, and some new best practices -- **✨ 89,000 stars**: Blushing, surprised and proud! +- **✨ Easily focus on new content**: Already visited before? Search for `#new` or `#updated` tags for new content only -- **🔖 New menu and tags**: Our menu is collapsible now and includes `#tags`. New visitors can read `#strategic` items first. Returning visitors can focus on `#new` content. Seniors can filter for `#advanced` items. Courtesy of the one and only [Rubek Joshi](https://github.com/rubek-joshi) +- **🔖 Curious to see examples? We have a starter**: Visit [Practica.js](https://github.com/practicajs/practica), our application example and boilerplate (beta) to see some practices in action -- **![FR](./assets/flags/FR.png) French translation!1! :** The latest translation that joins our international guide is French. Bienvenue

    @@ -307,7 +302,7 @@ my-system ## ![✔] 1.5 Consider all the consequences when choosing the main framework -### `🌟 New item` +### `🌟 #new` **TL;DR:** When building apps and APIs, using a framework is mandatory. It's easy to overlook alternative frameworks or important considerations and then finally land on a sub optimal option. As of 2023/2024, we believe that these four frameworks are worth considering: [Nest.js](https://nestjs.com/), [Fastify](https://www.fastify.io/), [express](https://expressjs.com/), and [Koa](https://koajs.com/). Click read more below for a detailed pros/cons of each framework. Simplistically, we believe that Nest.js is the best match for teams who wish to go OOP and/or build large-scale apps that can't get partitioned into smaller _autonomous_ components. Fastify is our recommendation for apps with reasonably-sized components (e.g., Microservices) that are built around simple Node.js mechanics. Read our [full considerations guide here](./sections/projectstructre/choose-framework.md) @@ -317,7 +312,7 @@ my-system ## ![✔] 1.6 Use TypeScript sparingly and thoughtfully -### `🌟 New item` +### `🌟 #new` **TL;DR:** Coding without type safety is no longer an option, TypeScript is the most popular option for this mission. Use it to define variables and functions return types. With that, it is also a double edge sword that can greatly _encourage_ complexity with its additional ~ 50 keywords and sophisticated features. Consider using it sparingly, mostly with simple types, and utilize advanced features only when a real need arises @@ -453,7 +448,7 @@ my-system ## ![✔] 2.12 Always await promises before returning to avoid a partial stacktrace -### `🌟 New item` +### `🌟 #new` **TL;DR:** Always do `return await` when returning a promise to benefit full error stacktrace. If a function returns a promise, that function must be declared as `async` function and explicitly @@ -702,7 +697,7 @@ All statements above will return false if used with `===` ## ![✔] 3.13 Avoid effects outside of functions -### `🌟 New item` +### `🌟 #new` **TL;DR:** Avoid putting code with effects like network or DB calls outside of functions. Such a code will be executed immediately when another file requires the file. This 'floating' code might get executed when the underlying system is not ready yet. It also comes with a performance penalty even when this module's functions will finally not be used in runtime. Last, mocking these DB/network calls for testing is harder outside of functions. Instead, put this code inside functions that should get called explicitly. If some DB/network code must get executed right when the module loads, consider using the factory or revealing module patterns @@ -730,7 +725,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.2 Include 3 parts in each test name - ### `🌟 New item` + ### `🌟 #new` **TL;DR:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result @@ -742,7 +737,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.3 Structure tests by the AAA pattern -### `🌟 New item` +### `🌟 #new` **TL;DR:** Structure your tests with 3 well-separated sections: Arrange, Act & Assert (AAA). The first part includes the test setup, then the execution of the unit under test, and finally the assertion phase. Following this structure guarantees that the reader spends no brain CPU on understanding the test plan @@ -754,7 +749,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.4 Ensure Node version is unified -### `🌟 New item` +### `🌟 #new` **TL;DR:** Use tools that encourage or enforce the same Node.js version across different environments and developers. Tools like [nvm](https://github.com/nvm-sh/nvm), and [Volta](https://volta.sh/) allow specifying the project's version in a file so each team member can run a single command to conform with the project's version. Optionally, this definition can be replicated to CI and the production runtime (e.g., copy the specified value to .Dockerfile build and to the CI declaration file) @@ -808,7 +803,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.10 Mock responses of external HTTP services -### `🌟 New item` +### `🌟 #new` **TL;DR:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production @@ -826,7 +821,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.12 Specify a port in production, randomize in testing -### `🌟 New item` +### `🌟 #new` **TL;DR:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port @@ -836,7 +831,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.13 Test the five possible outcomes -### `🌟 New item` +### `🌟 #new` **TL;DR:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) @@ -1355,7 +1350,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 6.27. Import built-in modules using the 'node:' protocol -### `🌟 New item` +### `🌟 #new` @@ -1540,7 +1535,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.11. Clean-out build-time secrets, avoid secrets in args -### `🌟 New item` +### `🌟 #new` **TL;DR:** Avoid secrets leaking from the Docker build environment. A Docker image is typically shared in multiple environment like CI and a registry that are not as sanitized as production. A typical example is an npm token which is usually passed to a dockerfile as argument. This token stays within the image long after it is needed and allows the attacker indefinite access to a private npm registry. This can be avoided by coping a secret file like `.npmrc` and then removing it using multi-stage build (beware, build history should be deleted as well) or by using Docker build-kit secret feature which leaves zero traces @@ -1580,7 +1575,7 @@ In addition, referring to an image tag means that the base image is subject to c ## ![✔] 8.15. Lint your Dockerfile -### `🌟 New item` +### `🌟 #new` **TL;DR:** Linting your Dockerfile is an important step to identify issues in your Dockerfile which differ from best practices. By checking for potential flaws using a specialised Docker linter, performance and security improvements can be easily identified, saving countless hours of wasted time or security issues in production code. From 5e2c5bc007881fb071ad62ea00c437aa0bc8e452 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Thu, 20 Jul 2023 15:46:16 +0300 Subject: [PATCH 1762/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6879f41b..913026604 100644 --- a/README.md +++ b/README.md @@ -677,7 +677,7 @@ All statements above will return false if used with `===` ## ![✔] 3.11 Use Async Await, avoid callbacks -**TL;DR:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch +**TL;DR:** Async-await is the simplest way to express an asynchronous flow as it makes asynchronous code look synchronous. Async-await will also result in much more compact code and support for try-catch. This technique now supersedes callbacks and promises in _most_ cases. Using it in your code is probably the best gift one can give to the code reader **Otherwise:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow From b11ca9df0e0d0d85e91a7843d9d2eeba4d54aea6 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Fri, 21 Jul 2023 08:46:29 +0300 Subject: [PATCH 1763/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 913026604..22ea296f1 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@
    - 102 items Last update: April 19, 2023 Updated for Node 14.0.0 + 102 items Last update: July 19, 2023 Updated for Node 19.0.0

    From 51f869e6c58d960cfec8c8b9de815a79649ceb5a Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 12:07:10 +0300 Subject: [PATCH 1764/1795] translate section 3.11 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 68b4e9e42..011fd614c 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -99,7 +99,7 @@   [3.8 טענו מודולים בתחילה, ולא בקריאה לפונקציות](#-38-require-modules-first-not-inside-functions)
      [3.9 הגדירו כניסה מסודרת לספריה שלכם `#updated`](#-39-set-an-explicit-entry-point-to-a-modulefolder)
      [3.10 השתמשו באופרטור `===`](#-310-use-the--operator)
    -  [3.11 Use Async Await, avoid callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
    +  [3.11 השתמשו ב-Async Await, המנעו מ-callbacks `#strategic`](#-311-use-async-await-avoid-callbacks)
      [3.12 השתמשו בפונקציות חץ (=>)](#-312-use-arrow-function-expressions-)
      [3.13 הימנעו מהשפעות צדדיות מחוץ לפונקציות `#new`](#-313-avoid-effects-outside-of-functions)
    @@ -659,13 +659,13 @@ null == undefined; // true

    -## ![✔] 3.11 Use Async Await, avoid callbacks +## ![✔] 3.11 השתמשו ב-Async Await, המנעו מ-callbacks -**אמ;לק:** Node 8 LTS now has full support for Async-await. This is a new way of dealing with asynchronous code which supersedes callbacks and promises. Async-await is non-blocking, and it makes asynchronous code look synchronous. The best gift you can give to your code is using async-await which provides a much more compact and familiar code syntax like try-catch +**אמ;לק:** async-await זו הדרך הפשוטה ביותר לכתוב קוד אסינכרוני שירגיש כמו קוד סינכרוני. הקוד שיכתב בשיטת async-await הוא גם הרבה יותר פשוט ותומך במנגנון ה-try-catch. שיטה זו מחליפה את הצורך ב-callbacks ו-promises ברוב המקרים. שימוש בשיטה זו בקוד היא כנראה אחת המתנות הטובות יותר שאפשר לתת למי שיקרא את הקוד. -**אחרת:** Handling async errors in callback style are probably the fastest way to hell - this style forces to check errors all over, deal with awkward code nesting, and makes it difficult to reason about the code flow +**אחרת:** טיפול בשגיאות אסינכרוניות בשיטת callback היא כנראה הדרך המהירה לגהנום - מכיוון ששיטה זו מחייבת בדיקת שגיאות בכל שלב, יוצרת קינון מוזר בקוד ומקשה על הבנת תהליך הזרימה של הקוד. -🔗[**Read more:** Guide to async-await 1.0](https://github.com/yortus/asyncawait) +🔗[**לקריאה נוספת:** מדריך ל-async-await](https://github.com/yortus/asyncawait)

    From 65194bfc2757200be642424af9ce9fbda4af7d79 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 12:43:43 +0300 Subject: [PATCH 1765/1795] translate section 4.9 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 011fd614c..b7c2b0586 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -118,7 +118,7 @@   [4.6 תייגו את הבדיקות `#advanced`](#-46-tag-your-tests)
      [4.7 בידקו את רמת כיסוי הבדיקות שלכם, זה יעזור לזהות דפוסי בדיקות שגויים](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
    -  [4.9 Refactor regularly using static analysis tools](#-49-refactor-regularly-using-static-analysis-tools)
    +  [4.9 שכתבו את הקוד באופן קבוע בעזרת כלי ניתוח סטטי](#-49-refactor-regularly-using-static-analysis-tools)
      [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
      [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
    @@ -767,13 +767,13 @@ null == undefined; // true

    -## ![✔] 4.9 Refactor regularly using static analysis tools +## ![✔] 4.9 שכתבו את הקוד באופן קבוע בעזרת כלי ניתוח סטטי -**אמ;לק:** Using static analysis tools helps by giving objective ways to improve code quality and keeps your code maintainable. You can add static analysis tools to your CI build to fail when it finds code smells. Its main selling points over plain linting are the ability to inspect quality in the context of multiple files (e.g. detect duplications), perform advanced analysis (e.g. code complexity), and follow the history and progress of code issues. Two examples of tools you can use are [Sonarqube](https://www.sonarqube.org/) (2,600+ [stars](https://github.com/SonarSource/sonarqube)) and [Code Climate](https://codeclimate.com/) (1,500+ [stars](https://github.com/codeclimate/codeclimate)). +**אמ;לק:** שימוש בכלי ניתוח סטטי (static analysis tools) עוזר בכך שהוא נותן דרכים מתאימות לשפר את איכות הקוד ולשמור על הקוד מתוחזק. אפשר להוסיף כלים כאלו לשלבי הבנייה ב-CI כך שיפילו את התהליך במידה והם מזהים ניחוחות בקוד. אחד היתרונות העיקריים שלהם על פני כלים פשוטים יותר הוא היכולת לזהות פגמים באיכות הקוד על פני מספר קבצים (כמו כפל קוד), מורכבות גבוהה של קוד ומעקב אחרי ההיסטוריה וההתקדמות של הקוד. שני כלים מומלצים לשימוש הם [Sonarqube](https://www.sonarqube.org/) (7,900+ [stars](https://github.com/SonarSource/sonarqube)) ו [Code Climate](https://codeclimate.com/) (2,400+ [stars](https://github.com/codeclimate/codeclimate)). -**אחרת:** With poor code quality, bugs and performance will always be an issue that no shiny new library or state of the art features can fix +**אחרת:** אם הקוד באיכות נמוכה, תקלות ובעיות ביצועים תמיד יהוו אתגר שאף ספריה חדשה ונוצצת או פתרון טכנולוגי חדיש יוכלו לפתור. -🔗 [**Read More: Refactoring!**](./sections/testingandquality/refactoring.md) +🔗 [**לקריאה נוספת: שכתוב!**](./sections/testingandquality/refactoring.md)

    From e4d858b918bcfc236229c3b1ca4fc7ba6a33f451 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 14:51:52 +0300 Subject: [PATCH 1766/1795] translate section 4.10 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index b7c2b0586..a08df87fb 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -119,7 +119,7 @@   [4.7 בידקו את רמת כיסוי הבדיקות שלכם, זה יעזור לזהות דפוסי בדיקות שגויים](#-47-check-your-test-coverage-it-helps-to-identify-wrong-test-patterns)
      [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
      [4.9 שכתבו את הקוד באופן קבוע בעזרת כלי ניתוח סטטי](#-49-refactor-regularly-using-static-analysis-tools)
    -  [4.10 Mock responses of external HTTP services #advanced `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
    +  [4.10 הדמיית תשובות של שרתי HTTP חיצוניים `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
      [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
      [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    @@ -777,13 +777,13 @@ null == undefined; // true

    -## ![✔] 4.10 Mock responses of external HTTP services +## ![✔] 4.10 הדמיית תשובות של שרתי HTTP חיצוניים -**אמ;לק:** Use network mocking tools to simulate responses of external collaborators' services that are approached over the network (e.g., REST, Graph). This is imperative not only to isolate the component under test but mostly to simulate non-happy path flows. Tools like [nock](https://github.com/nock/nock) (in-process) or [Mock-Server](https://www.mock-server.com/) allow defining a specific response of external service in a single line of code. Remember to simulate also errors, delays, timeouts, and any other event that is likely to happen in production +**אמ;לק:** השתמשו בכלי הדמיה של המידע שמגיע מהרשת עבור תשובות שמגיעות משירותים חיצוניים (כמו בקשות REST ו GraphQL). זה הכרחי לא רק כדי לבודד את הרכיב שנבדק אלא בעיקר כדי לבדוק מצבים לא צפויים. כלים כמו [nock](https://github.com/nock/nock) או [Mock-Server](https://www.mock-server.com/) מאפשרים להגדיר תשובה מסוימת לבקשה לשירות חיצוני בשורת קוד בודדה. חשוב לא לשכוח לדמות גם שגיאות, עיכובים, timeouts, וכל אירוע אחר שכנראה יקרה בסביבת הייצור. -**אחרת:** Allowing your component to reach real external services instances will likely result in naive tests that mostly cover happy paths. The tests might also be flaky and slow +**אחרת:** לאפשר לרכיב לגשת למידע אמיתי משירותים חיצוניים בדרך כלל יסתיים בבדיקות פשוטות שמכסות בעיקר את המקרים שהכל טוב. בנוסף לכך הבדיקות לפעמים יכשלו ויהיו איטיות יותר. -🔗 [**Read More: Mock external services**](./sections/testingandquality/mock-external-services.md) +🔗 [**לקריאה נוספת: הדמיית שירותים חיצוניים**](./sections/testingandquality/mock-external-services.md) ## ![✔] 4.11 Test your middlewares in isolation From 8891402c5934eef6b4dd7f6c3d5f2d24ddfc5966 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 15:01:34 +0300 Subject: [PATCH 1767/1795] translate section 4.11 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index a08df87fb..8965ca899 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -120,7 +120,7 @@   [4.8 Use production-like environment for e2e testing](#-48-use-production-like-environment-for-e2e-testing)
      [4.9 שכתבו את הקוד באופן קבוע בעזרת כלי ניתוח סטטי](#-49-refactor-regularly-using-static-analysis-tools)
      [4.10 הדמיית תשובות של שרתי HTTP חיצוניים `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
    -  [4.11 Test your middlewares in isolation](#-411-test-your-middlewares-in-isolation)
    +  [4.11 בדקו את פונקציות הביניים בנפרד](#-411-test-your-middlewares-in-isolation)
      [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
      [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    @@ -785,13 +785,13 @@ null == undefined; // true 🔗 [**לקריאה נוספת: הדמיית שירותים חיצוניים**](./sections/testingandquality/mock-external-services.md) -## ![✔] 4.11 Test your middlewares in isolation +## ![✔] 4.11 בידקו את פונקציות הביניים בנפרד -**אמ;לק:** When a middleware holds some immense logic that spans many requests, it is worth testing it in isolation without waking up the entire web framework. This can be easily achieved by stubbing and spying on the {req, res, next} objects +**אמ;לק:** כאשר פונקציית ביניים (middleware) אוחזת נתח משמעותי של לוגיקה שמשתרעת על פני מספר עצום של בקשות, כדאי לבדוק אותה בצורה מבודדת ללא צורך לטעון את כל תשתית הפריימוורק. אפשר להשיג את הפעולה הזאת בקלות על ידי עטיפה או הדמיה של `{req, res, next}`. -**אחרת:** A bug in Express middleware === a bug in all or most requests +**אחרת:** באג בפונקציות ביניים ב-`express` === באג ברוב הקריטי של הבקשות. -🔗 [**Read More: Test middlewares in isolation**](./sections/testingandquality/test-middlewares.md) +🔗 [**לקריאה נוספת: לבדוק פונקציות ביניים בנפרד**](./sections/testingandquality/test-middlewares.md) ## ![✔] 4.12 Specify a port in production, randomize in testing From 33d427183311ae786f5f9a35e7c6f8ed66cefb02 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 15:33:45 +0300 Subject: [PATCH 1768/1795] translate section 4.12 --- README.hebrew.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 8965ca899..ea726c62f 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -121,7 +121,7 @@   [4.9 שכתבו את הקוד באופן קבוע בעזרת כלי ניתוח סטטי](#-49-refactor-regularly-using-static-analysis-tools)
      [4.10 הדמיית תשובות של שרתי HTTP חיצוניים `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 בדקו את פונקציות הביניים בנפרד](#-411-test-your-middlewares-in-isolation)
    -  [4.12 Specify a port in production, randomize in testing `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
    +  [4.12 קבעו את הפורט בייצור, הגדירו אקראי לבדיקות `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
      [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    @@ -785,6 +785,8 @@ null == undefined; // true 🔗 [**לקריאה נוספת: הדמיית שירותים חיצוניים**](./sections/testingandquality/mock-external-services.md) +

    + ## ![✔] 4.11 בידקו את פונקציות הביניים בנפרד **אמ;לק:** כאשר פונקציית ביניים (middleware) אוחזת נתח משמעותי של לוגיקה שמשתרעת על פני מספר עצום של בקשות, כדאי לבדוק אותה בצורה מבודדת ללא צורך לטעון את כל תשתית הפריימוורק. אפשר להשיג את הפעולה הזאת בקלות על ידי עטיפה או הדמיה של `{req, res, next}`. @@ -793,13 +795,17 @@ null == undefined; // true 🔗 [**לקריאה נוספת: לבדוק פונקציות ביניים בנפרד**](./sections/testingandquality/test-middlewares.md) -## ![✔] 4.12 Specify a port in production, randomize in testing +

    + +## ![✔] 4.12 קבעו את הפורט בייצור, הגדירו אקראי לבדיקות + +**אמ;לק:** כאשר מבצעים בדיקות מול API, זה רצוי ואף נהוג לאתחל את השרת בתוך הבדיקות. תנו לשרת לבחור פורט באופן אקראי כאשר מריצים בדיקות כדי למנוע התנגשויות. אם אתם משתמשים בשרת HTTP של Node.js (בשימוש על ידי רוב ספריות התשתית), כדי להשיג את היכולת הזאת אין צורך לעשות כלום מלבד להעביר port=0 - זה כבר יגרום להקצאה דינאמית של פורט. -**אמ;לק:** When testing against the API, it's common and desirable to initialize the web server inside the tests. Let the server randomize the web server port in testing to prevent collisions. If you're using Node.js http server (used by most frameworks), doing so demands nothing but passing a port number zero - this will randomize an available port +**אחרת:** הגדרה של פורט ספציפי ימנע את האפשרות להריץ שני טסטים במקביל. רוב הכלים שמריצים כיום טסטים - מריצים במקביל כברירת מחדל. -**אחרת:** Specifying a fixed port will prevent two testing processes from running at the same time. Most of the modern test runners run with multiple processes by default +🔗 [**לקריאה נוספת: הגדירו פורט אקראי לבדיקות**](./sections/testingandquality/randomize-port.md) -🔗 [**Read More: Randomize a port for testing**](./sections/testingandquality/randomize-port.md) +

    ## ![✔] 4.13 Test the five possible outcomes From 3a19bf40db9aeb16e8b492111df0d9243adfd847 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Mon, 24 Jul 2023 17:16:49 +0300 Subject: [PATCH 1769/1795] Update README.md --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 22ea296f1..8845bd835 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin   [2.10 Catch unhandled promise rejections `#updated`](#-210-catch-unhandled-promise-rejections)
      [2.11 Fail fast, validate arguments using a dedicated library](#-211-fail-fast-validate-arguments-using-a-dedicated-library)
      [2.12 Always await promises before returning to avoid a partial stacktrace `#new`](#-212-always-await-promises-before-returning-to-avoid-a-partial-stacktrace)
    +  [2.13 Subscribe to event emitters 'error' event `#new`](#-213-subscribe-to-event-emitters-and-streams-error-event)
    @@ -460,6 +461,16 @@ especially if the cause of the abnormal behavior is inside of the missing functi 🔗 [**Read More: returning promises**](./sections/errorhandling/returningpromises.md) +

    + +## ![✔] 2.13 Subscribe to event emitters and streams 'error' event + +### `🌟 #new` + +**TL;DR:** Unlike typical functions, a try-catch clause won't get errors that originate from Event Emitters and anything inherited from it (e.g., streams). Instead of try-catch, subscribe to an event emitter's 'error' event so your code can handle the error in context. When dealing with [EventTargets](https://nodejs.org/api/events.html#eventtarget-and-event-api) (the web standard version of Event Emitters) there are no 'error' event and all errors end in the process.on('error) global event - in this case, at least ensure that the process crash or not based on the desired context. Also, mind that error originating from _asynchronous_ event handlers are not get caught unless the event emitter is initialized with {captureRejections: true} + +**Otherwise:** Event emitters are commonly used for global and key application functionality such as DB or message queue connection. When this kind of crucial objects throw an error, at best the process will crash due to unhandled exception. Even worst, it will stay alive as a zombie while a key functionality is turned off +


    ⬆ Return to top

    From d465007bb38d14bfbd72a958cfa8c27c31a00305 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 24 Jul 2023 17:43:16 +0300 Subject: [PATCH 1770/1795] translate section 4.13 --- README.hebrew.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index ea726c62f..b796adcf6 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -122,7 +122,7 @@   [4.10 הדמיית תשובות של שרתי HTTP חיצוניים `#new` `#advanced`](#-410-mock-responses-of-external-http-services)
      [4.11 בדקו את פונקציות הביניים בנפרד](#-411-test-your-middlewares-in-isolation)
      [4.12 קבעו את הפורט בייצור, הגדירו אקראי לבדיקות `#new`](#-412-specify-a-port-in-production-randomize-in-testing)
    -  [4.13 Test the five possible outcomes #strategic `#new`](#-413-test-the-five-possible-outcomes)
    +  [4.13 בידקו את חמשת התוצאות האפשריות `#strategic` `#new`](#-413-test-the-five-possible-outcomes)
    @@ -694,7 +694,7 @@ null == undefined; // true > יש לנו מדריכים יעודיים לכתיבת בדיקות. רשימת שיטות העבודה המומלצות פה היא סיכום כללי של המדריכים הללו. > > א. [שיטות עבודה מומלצות בכתיבת בדיקות ל-JavaScript](https://github.com/goldbergyoni/javascript-testing-best-practices)
    -> ב. [בדיקות ב-Node.js - אחרי היסודות](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +> ב. [בדיקות ב-Node.js - מעבר ליסודות](https://github.com/testjavascript/nodejs-integration-tests-best-practices) ## ![✔] 4.1 לפחות, כיתבו בדיקות API לרכיבים השונים @@ -807,13 +807,14 @@ null == undefined; // true

    -## ![✔] 4.13 Test the five possible outcomes +## ![✔] 4.13 בידקו את חמשת התוצאות האפשריות -**אמ;לק:** When testing a flow, ensure to cover five potential categories. Any time some action is triggered (e.g., API call), a reaction occurs, a meaningful **outcome** is produced and calls for testing. There are five possible outcome types for every flow: a response, a visible state change (e.g., DB), an outgoing API call, a new message in a queue, and an observability call (e.g., logging, metric). See a [checklist here](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). Each type of outcome comes with unique challenges and techniques to mitigate those challenges - we have a dedicated guide about this topic: [Node.js testing - beyond the basics](https://github.com/testjavascript/nodejs-integration-tests-best-practices) +**אמ;לק:** בעת בדיקת מקרה, ודאו שאתם מכסים את חמשת הקטגוריות האפשריות. בכל פעם שפעולה חלה (למשל קריאת API), מתחילה תגובה, **תוצאה** משמעותית נוצרת ומתבצעת קריאה לבדיקה. ישנן חמש סוגי תוצאות לכל מקרה: תגובה, שינוי נראה לעין (כמו עדכון במסד הנתונים), שליחת קריאה ל- +API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה במידע (כמו לוגר ואנליטיקות). [רשימת בדיקות בסיסיות](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf). כל סוג של תוצאה מגיע אם אתגרים יחודיים ושיטות להמתיק את האתגרים הללו - כתבנו מדריך יעודי על נושא זה [בדיקות ב-Node.js - מעבר ליסודות](https://github.com/testjavascript/nodejs-integration-tests-best-practices) -**אחרת:** Consider a case when testing the addition of a new product to the system. It's common to see tests that assert on a valid response only. What if the product was failed to persist regardless of the positive response? what if when adding a new product demands calling some external service, or putting a message in the queue - shouldn't the test assert these outcomes as well? It's easy to overlook various paths, this is where a [checklist comes handy](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) +**אחרת:** תארו לעצמכם מקרה של בדיקת הוספה של מוצר חדש למערכת. נפוץ לראות בדיקות שמכסות אך ורק את המקרים של תשובה תקינה. מה יקרה אם המוצר לא יתווסף על אף התשובה החיובית? מה צריך להיעשות במידה ובעת הוספת מוצר יש גם קריאה לשירות חיצוני או הוספת הודעה לתור - האם הבדיקה לא צריכה להתייחס גם לזה? קל להתעלם ממגוון מקרים, ובנקודה זאת [רשימת הבדיקות](https://testjavascript.com/wp-content/uploads/2021/10/the-backend-checklist.pdf) עוזרת. -🔗 [**Read More: Test five outcomes**](./sections/testingandquality/test-five-outcomes.md) +🔗 [**לקריאה נוספת: בדיקת חמשת התוצאות**](./sections/testingandquality/test-five-outcomes.md)


    From fea4adafa44b7c7dd0743f20f6da46496b103f6d Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 25 Jul 2023 15:57:01 +0300 Subject: [PATCH 1771/1795] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8845bd835..37de2bc05 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin

    +# By Yoni Goldberg + +## Learn with me: As a consultant, I engage with worldwide teams on various activities like workshops and code reviews. 🎉AND... Hold on, I've just launched my [beyond-the-basics testing course, which is on a 🎁 limited-time sale](https://testjavascript.com/) until August 7th + +

    + ## Table of Contents
    From c72f74aa3e4a6669c8c00ece68d1fbfd846990e0 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Tue, 25 Jul 2023 16:18:06 +0300 Subject: [PATCH 1772/1795] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 37de2bc05..50a96d82e 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin # By Yoni Goldberg -## Learn with me: As a consultant, I engage with worldwide teams on various activities like workshops and code reviews. 🎉AND... Hold on, I've just launched my [beyond-the-basics testing course, which is on a 🎁 limited-time sale](https://testjavascript.com/) until August 7th +### Learn with me: As a consultant, I engage with worldwide teams on various activities like workshops and code reviews. 🎉AND... Hold on, I've just launched my [beyond-the-basics testing course, which is on a 🎁 limited-time sale](https://testjavascript.com/) until August 7th

    From 4c632f59f42aa14442018c398e7c2756a04c8be7 Mon Sep 17 00:00:00 2001 From: muratcan Date: Wed, 26 Jul 2023 20:16:56 +0300 Subject: [PATCH 1773/1795] fix code sample for example 3.3 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 50a96d82e..324f9ba7e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,6 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin - **🔖 Curious to see examples? We have a starter**: Visit [Practica.js](https://github.com/practicajs/practica), our application example and boilerplate (beta) to see some practices in action -

    # Welcome! 3 Things You Ought To Know First @@ -516,7 +515,8 @@ function someFunction() { } // Avoid -function someFunction() { +function someFunction() +{ // code block } ``` @@ -742,7 +742,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 4.2 Include 3 parts in each test name - ### `🌟 #new` +### `🌟 #new` **TL;DR:** Make the test speak at the requirements level so it's self-explanatory also to QA engineers and developers who are not familiar with the code internals. State in the test name what is being tested (unit under test), under what circumstances, and what is the expected result From fedb0bd586195dae78b9bf8a20042b4015eea389 Mon Sep 17 00:00:00 2001 From: muratcan Date: Wed, 26 Jul 2023 20:17:56 +0300 Subject: [PATCH 1774/1795] fix code sample for 3.3 in polish --- README.polish.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.polish.md b/README.polish.md index 8ac673428..3fdae83d2 100644 --- a/README.polish.md +++ b/README.polish.md @@ -256,7 +256,8 @@ function someFunction() { } // Avoid -function someFunction() { +function someFunction() +{ // code block } ``` From 27032a913f87810bf6685976f5ed96d4f5100b16 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Fri, 28 Jul 2023 00:17:14 +0900 Subject: [PATCH 1775/1795] Fix typo in readme-general-toc-4.md Partioning -> Partitioning --- sections/drafts/readme-general-toc-4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/drafts/readme-general-toc-4.md b/sections/drafts/readme-general-toc-4.md index 85ec9ce0f..3db7b68a7 100644 --- a/sections/drafts/readme-general-toc-4.md +++ b/sections/drafts/readme-general-toc-4.md @@ -25,7 +25,7 @@ Welcome to the biggest compilation of Node.js best practices, based on our check ## ![](../../assets/images/checkbox-sm.png) 1. Structure your solution by feature ('microservices') -**TL&DR:** The worst large applications pitfal is a huge code base where hundreds of dependencies slow down developers as try to incorporate new features. Partioning into small units ensures that each unit is kept simple and very easy to maintain. This strategy pushes the complexity to the higher level - designing the cross-component interactions. +**TL&DR:** The worst large applications pitfal is a huge code base where hundreds of dependencies slow down developers as try to incorporate new features. Partitioning into small units ensures that each unit is kept simple and very easy to maintain. This strategy pushes the complexity to the higher level - designing the cross-component interactions. **Otherwise:** Developing a new feature with a change to few objects demands to evaluate how this changes might affect dozends of dependants and ach deployment becomes a fear. From d7490ba061ccfea70b82aaaa9eaacd22cb74d3ee Mon Sep 17 00:00:00 2001 From: Abdeldjalil Hebal Date: Fri, 28 Jul 2023 11:27:18 +0100 Subject: [PATCH 1776/1795] Fix typos and grammar in returningpromises.md --- sections/errorhandling/returningpromises.md | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sections/errorhandling/returningpromises.md b/sections/errorhandling/returningpromises.md index 9fe85566b..11ebc5a67 100644 --- a/sections/errorhandling/returningpromises.md +++ b/sections/errorhandling/returningpromises.md @@ -4,7 +4,7 @@ ### One Paragraph Explainer -When an error occurs, whether from a synchronous or asynchronous flow, it's imperative to have a full stacktrace of the error flow. Surprisingly, if an async function returns a promise (e.g. calls other async function) without awaiting, should an error occur then the caller function won't appear in the stacktrace. This will leave the person who diagnoses the error with partial information - All the more if the error cause lies within that caller function. There is a feature v8 called "zero-cost async stacktraces" that allow stacktraces not to be cut on the most recent `await`. But due to non-trivial implementation details, it will not work if the return value of a function (sync or async) is a promise. So, to avoid holes in stacktraces when returned promises would be rejected, we must always explicitly resolve promises with `await` before returning them from functions +When an error occurs, whether from a synchronous or asynchronous flow, it's imperative to have a full stacktrace of the error flow. Surprisingly, if an async function returns a promise (e.g. calls other async function) without awaiting, should an error occur then the caller function won't appear in the stacktrace. This will leave the person who diagnoses the error with partial information - All the more if the error cause lies within that caller function. There is a v8 feature called "zero-cost async stacktraces" that allows stacktraces to not be cut on the most recent `await`. But due to non-trivial implementation details, it will not work if the return value of a function (sync or async) is a promise. So, to avoid holes in stacktraces when returned promises would be rejected, we must always explicitly resolve promises with `await` before returning them from functions
    @@ -87,7 +87,7 @@ async function asyncFn () { return await syncFn() } -// 👎 syncFn would be missing in the stacktrace because it returns a promise while been sync +// 👎 syncFn would be missing in the stacktrace because it returns a promise while being sync asyncFn().catch(console.log) ``` @@ -165,7 +165,7 @@ Error: stacktrace is missing the place where getUser has been called at async Promise.all (index 2) ``` -*Side-note*: it may looks like `Promise.all (index 2)` can help understanding the place where `getUser` has been called, +*Side-note*: it may look like `Promise.all (index 2)` can help understanding the place where `getUser` has been called, but due to a [completely different bug in v8](https://bugs.chromium.org/p/v8/issues/detail?id=9023), `(index 2)` is a line from internals of v8 @@ -177,9 +177,9 @@ a line from internals of v8
    Javascript

    -*Note 1*: in case if you control the code of the function that would call the callback - just change that function to -async and add `await` before the callback call. Below I assume that you are not in charge of the code that is calling -the callback (or it's change is unacceptable for example because of backward compatibility) +*Note 1*: if you control the code of the function that would call the callback - just change that function to +`async` and add `await` before the callback call. Below I assume that you are not in charge of the code that is calling +the callback (or its change is unacceptable for example because of backward compatibility) *Note 2*: quite often usage of async callback in places where sync one is expected would not work at all. This is not about how to fix the code that is not working - it's about how to fix stacktrace in case if code is already working as @@ -210,8 +210,8 @@ Error: with all frames present where thanks to explicit `await` in `map`, the end of the line `at async ([...])` would point to the exact place where `getUser` has been called -*Side-note*: if async function that wrap `getUser` would miss `await` before return (anti-pattern #1 + anti-pattern #3) -then only one frame would left in the stacktrace: +*Side-note*: if async function that wrap `getUser` lacks `await` before return (anti-pattern #1 + anti-pattern #3) +then only one frame would be left in the stacktrace: ```javascript [...] @@ -235,12 +235,12 @@ Error: [...] ## Advanced explanation The mechanisms behind sync functions stacktraces and async functions stacktraces in v8 implementation are quite different: -sync stacktrace is based on **stack** provided by operating system Node.js is running on (just like in most programming -languages). When an async function is executing, the **stack** of operating system is popping it out as soon as the -function is getting to it's first `await`. So async stacktrace is a mix of operating system **stack** and a rejected -**promise resolution chain**. Zero-cost async stacktraces implementation is extending the **promise resolution chain** +sync stacktrace is based on **stack** provided by the operating system Node.js is running on (just like in most programming +languages). When an async function is executing, the **stack** of the operating system is popping it out as soon as the +function gets to its first `await`. So async stacktrace is a mix of operating system **stack** and a rejected +**promise resolution chain**. Zero-cost async stacktraces implementation extends the **promise resolution chain** only when the promise is getting `awaited` [¹](#1). Because only `async` functions may `await`, -sync function would always be missed in async stacktrace if any async operation has been performed after the function +sync function would always be missing from async stacktrace if any async operation has been performed after the function has been called [²](#2) ### The tradeoff @@ -256,13 +256,13 @@ definitely should never be done up-front ### Why return await was considered as anti-pattern in the past -There is a number of [excellent articles](https://jakearchibald.com/2017/await-vs-return-vs-return-await/) explained +There is a number of [excellent articles](https://jakearchibald.com/2017/await-vs-return-vs-return-await/) explaining why `return await` should never be used outside of `try` block and even an [ESLint rule](https://eslint.org/docs/rules/no-return-await) that disallows it. The reason for that is the fact that since async/await become available with transpilers in Node.js 0.10 (and got native support in Node.js 7.6) and until "zero-cost async stacktraces" was introduced in Node.js 10 and unflagged in Node.js 12, `return await` was absolutely equivalent to `return` for any code outside of `try` block. It may still be the same for some other ES engines. This -is why resolving promises before returning them is the best practice for Node.js and not for the EcmaScript in general +is why resolving promises before returning them is the best practice for Node.js and not for ECMAScript in general ### Notes: @@ -270,7 +270,7 @@ is why resolving promises before returning them is the best practice for Node.js must always be built synchronously, on the same tick of event loop [¹](#1) 2. Without `await` in `throwAsync` the code would be executed in the same phase of event loop. This is a degenerated case when OS **stack** would not get empty and stacktrace be full even without explicitly -awaiting the function result. Usually usage of promises include some async operations and so parts of +awaiting the function result. Common usage of promises includes some async operations and so parts of the stacktrace would get lost 3. Zero-cost async stacktraces still would not work for complicated promise usages e.g. single promise awaited many times in different places From 845dc5e9a387f5ec647ce9623e98d95e43473408 Mon Sep 17 00:00:00 2001 From: Regev Golan Date: Sun, 11 Jun 2023 16:57:02 +0300 Subject: [PATCH 1777/1795] [doc] Line break curly braces- Same line Avoid example --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d811388ee..5a776d14b 100644 --- a/README.md +++ b/README.md @@ -480,7 +480,8 @@ function someFunction() { } // Avoid -function someFunction() { +function someFunction() +{ // code block } ``` From c6581358c40e4a1d600d91544b6da33e3c6efe30 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 1 Aug 2023 10:09:59 +0300 Subject: [PATCH 1778/1795] translate section 5.1 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index b796adcf6..4eb46a990 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -131,7 +131,7 @@ 5. עלייה לאוויר (19) -  [5.1. Monitoring `#strategic`](#-51-monitoring)
    +  [5.1. ניטור `#strategic`](#-51-monitoring)
      [5.2. Increase the observability using smart logging `#strategic`](#-52-increase-the-observability-using-smart-logging)
      [5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. Lock dependencies](#-54-lock-dependencies)
    @@ -822,13 +822,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב # `5. עלייה לאוויר` -## ![✔] 5.1. Monitoring +## ![✔] 5.1. ניטור -**אמ;לק:** Monitoring is a game of finding out issues before customers do – obviously this should be assigned unprecedented importance. The market is overwhelmed with offers thus consider starting with defining the basic metrics you must follow (my suggestions inside), then go over additional fancy features and choose the solution that ticks all boxes. In any case, the 4 layers of observability must be covered: uptime, metrics with focus on user-facing symptoms and Node.js technical metrics like event loop lag, distributed flows measurement with Open Telemetry and logging. Click ‘Read More’ below for an overview of the solutions +**אמ;לק:** ניטור הוא משחק של מציאת בעיות לפני שהמשתמשים מוצאים אותן - מובן מאליו שזה צריך להיות בראש סדר העדיפויות. השוק מוצף בהצעות להגדרות מה הם המדדים הבסיסיים שחייבים לעקוב אחריהם (ההמלצות שלנו בהמשך), לאחר מכן לעבור על כל היכולות המעניינות שכל מוצר מציע ולבחור את הפתרון המיטבי עבור הדרישות שלכם. בכל מקרה, ארבעת השכבות הניתנות לצפייה חייבות להימדד: (1) Uptime - מציינת האם המערכת זמינה, (2) Metrics - מציינת מהי ההתנהגות המצטברת של המערכת (האם 99% מהבקשות נענות), (3) Logging - בודקת אם בקשה מסויימת מסתיימת בהצלחה, (4) Distributed tracing - בודקת האם המערכת יציבה בין הרכיבים המבוזרים שלה. -**אחרת:** Failure === disappointed customers. Simple +**אחרת:** כשלון === לקוחות מאוכזבים. פשוט מאוד. -🔗 [**Read More: Monitoring!**](./sections/production/monitoring.md) +🔗 [**לקריאה נוספת: ניטור!**](./sections/production/monitoring.md)

    From c2cf071aff98d0266857b7be06b0465c9150989a Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 1 Aug 2023 12:45:39 +0300 Subject: [PATCH 1779/1795] translate section 5.2 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 4eb46a990..c75c141c8 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -132,7 +132,7 @@   [5.1. ניטור `#strategic`](#-51-monitoring)
    -  [5.2. Increase the observability using smart logging `#strategic`](#-52-increase-the-observability-using-smart-logging)
    +  [5.2. הגדילו את יכולת הצפייה בעזרת לוגים איכותיים `#strategic`](#-52-increase-the-observability-using-smart-logging)
      [5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. Lock dependencies](#-54-lock-dependencies)
      [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
    @@ -832,13 +832,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.2. Increase the observability using smart logging +## ![✔] 5.2. הגדילו את יכולת הצפייה בעזרת לוגים איכותיים -**אמ;לק:** Logs can be a dumb warehouse of debug statements or the enabler of a beautiful dashboard that tells the story of your app. Plan your logging platform from day 1: how logs are collected, stored and analyzed to ensure that the desired information (e.g. error rate, following an entire transaction through services and servers, etc) can really be extracted +**אמ;לק:** לוגים יכולים להיות פח הזבל של שלל מצבים שהמפתחים רצו לדבג או לחלופין מסך מהמם שמתאר את המצב של המוצר. תכננו את הלוגים שלכם מהיום הראשון: איך הם נאספים, איפה הם נשמרים ואיך הם מנותחים כדי להבטיח שהמידע ההכרחי (אחוז שגיאות, מעקב אחר פעולה בין מספר שירותים וכו') באמת נגיש ובר שימוש. -**אחרת:** You end up with a black box that is hard to reason about, then you start re-writing all logging statements to add additional information +**אחרת:** יש לכם קופסה שחורה שקשה להבין למה היא מגיעה למצב הנוכחי, ורק עכשיו אתם מתחילים לשכתב את כל הלוגים שלכם כדי שיהיה מידע רלוונטי. -🔗 [**Read More: Increase transparency using smart logging**](./sections/production/smartlogging.md) +🔗 [**לקריאה נוספת: הגדלת השקיפות על ידי לוגים איכותיים**](./sections/production/smartlogging.md)

    From 37a1a446783a2c57d575437049cf1cf71f0fb444 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 1 Aug 2023 12:59:28 +0300 Subject: [PATCH 1780/1795] translate section 5.3 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index c75c141c8..f0776c903 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -133,7 +133,7 @@   [5.1. ניטור `#strategic`](#-51-monitoring)
      [5.2. הגדילו את יכולת הצפייה בעזרת לוגים איכותיים `#strategic`](#-52-increase-the-observability-using-smart-logging)
    -  [5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
    +  [5.3. האצילו כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. Lock dependencies](#-54-lock-dependencies)
      [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
    @@ -842,13 +842,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.3. Delegate anything possible (e.g. gzip, SSL) to a reverse proxy +## ![✔] 5.3. האצילו כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד -**אמ;לק:** Node is quite bad at doing CPU intensive tasks like gzipping, SSL termination, etc. You should use specialized infrastructure like nginx, HAproxy or cloud vendor services instead +**אמ;לק:** Node.js גרוע בלבצע פעולות שדורשות עוצמת חישוב גבוהה מה-CPU, כמו למשל דחיסה, סיום תהליך SSL, וכו'... כדאי שתשתמשו בתשתיות כמו nginx, HAproxy או שירותי ענן אחרים לשם כך. -**אחרת:** Your poor single thread will stay busy doing infrastructural tasks instead of dealing with your application core and performance will degrade accordingly +**אחרת:** הת'רד הבודד והמסכן שלכם יישאר עסוק במשימות תשתיתיות במקום להתעסק בלב המערכת שלכם והביצועים יישחקו בהתאם. -🔗 [**Read More: Delegate anything possible (e.g. gzip, SSL) to a reverse proxy**](./sections/production/delegatetoproxy.md) +🔗 [**לקריאה נוספת: האצלת כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד**](./sections/production/delegatetoproxy.md)

    From 154a31def9c60ba8714fcf4ec02414f53122ef7c Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 1 Aug 2023 14:55:58 +0300 Subject: [PATCH 1781/1795] translate section 5.4 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index f0776c903..0341e4f08 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -134,7 +134,7 @@   [5.1. ניטור `#strategic`](#-51-monitoring)
      [5.2. הגדילו את יכולת הצפייה בעזרת לוגים איכותיים `#strategic`](#-52-increase-the-observability-using-smart-logging)
      [5.3. האצילו כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
    -  [5.4. Lock dependencies](#-54-lock-dependencies)
    +  [5.4. קיבוע תלויות](#-54-lock-dependencies)
      [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
      [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    @@ -852,13 +852,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.4. Lock dependencies +## ![✔] 5.4. קיבוע תלויות -**אמ;לק:** Your code must be identical across all environments, but without a special lockfile npm lets dependencies drift across environments. Ensure to commit your package-lock.json so all the environments will be identical +**אמ;לק:** הקוד שלכם צריך להיות זהה בכל הסביבות, אך ללא קובץ יעודי npm יאפשר שימוש בתלויות שונות בכל סביבה. ודאו כי יש לכם `package-lock.json` כך שכל הסביבות יהיו זהות. -**אחרת:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code +**אחרת:** אנשי הבדיקות יאשרו גרסה שתתנהג אחרת בסביבת ייצור. גרוע מכך, שרתים שונים באותה סביבה יריצו קוד שונה. -🔗 [**Read More: Lock dependencies**](./sections/production/lockdependencies.md) +🔗 [**לקריאה נוספת: קיבוע תלויות**](./sections/production/lockdependencies.md)

    From f8534c64a856a69f6c4c0b6d38ebcb9a20e329f7 Mon Sep 17 00:00:00 2001 From: Anderson Joseph Date: Fri, 4 Aug 2023 19:30:58 -0400 Subject: [PATCH 1782/1795] fix broken env-var broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 324f9ba7e..a7163b1eb 100644 --- a/README.md +++ b/README.md @@ -298,7 +298,7 @@ my-system ### `📝 #updated` -**TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](env-var), [zod](https://github.com/colinhacks/zod), and others +**TL;DR:** A flawless configuration setup should ensure (a) keys can be read from file AND from environment variable (b) secrets are kept outside committed code (c) config is hierarchical for easier findability (d) typing support (e) validation for failing fast (f) Specify default for each key. There are a few packages that can help tick most of those boxes like [convict](https://www.npmjs.com/package/convict), [env-var](https://github.com/evanshortiss/env-var), [zod](https://github.com/colinhacks/zod), and others **Otherwise:** Consider a mandatory environment variable that wasn't provided. The app starts successfully and serve requests, some information is already persisted to DB. Then, it's realized that without this mandatory key the request can't complete, leaving the app in a dirty state From 1f581312e48ebd51797d1f6328c313732e9a1839 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 6 Aug 2023 14:41:22 +0300 Subject: [PATCH 1783/1795] translate section 5.5 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 0341e4f08..aa269fb16 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -135,7 +135,7 @@   [5.2. הגדילו את יכולת הצפייה בעזרת לוגים איכותיים `#strategic`](#-52-increase-the-observability-using-smart-logging)
      [5.3. האצילו כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. קיבוע תלויות](#-54-lock-dependencies)
    -  [5.5. Guard process uptime using the right tool](#-55-guard-process-uptime-using-the-right-tool)
    +  [5.5. הבטיחו את זמינות המערכת בעזרת הכלי המתאים](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
      [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
      [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
    @@ -862,13 +862,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.5. Guard process uptime using the right tool +## ![✔] 5.5. הבטיחו את זמינות המערכת בעזרת הכלי המתאים -**אמ;לק:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location +**אמ;לק:** המערכת צריכה להמשיך לעבוד ולהתאתחל במידה וקרתה שגיאה קריטית. סביבות ריצה חדשות כמו למשל כאלו המבוססות דוקר (כמו קוברנטיס), או Serverless מטפלות בזה בצורה אוטומטית. כאשר המוצר מותקן על שרת אמיתי פיזי, יש צורך לנהל את משאבי המערכת בעזרת כלי כמו [systemd](https://systemd.io/). אך יש להימנע מלעשות זאת כאשר משתמשים בתשתיות שכבר מבצעות את הניטור מכיוון שזה יגרום לבליעת שגיאות. כאשר לתשתית אין מודעות לשגיאות, אין לה יכולת של ביצוע שלבי פיחות משאבים כמו העברת האינסטנס של המערכת למקום אחר ברשת. -**אחרת:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos +**אחרת:** הרצה של עשרות אינסטנסים ללא סיבה ברורה ויותר מידי כלי תשתית יחד (cluster management, docker, PM2) עלול לגרום לכאוס עבור ה-DevOps. -🔗 [**Read More: Guard process uptime using the right tool**](./sections/production/guardprocess.md) +🔗 [**לקריאה נוספת: הבטיחו את זמינות המערכת בעזרת הכלי המתאים**](./sections/production/guardprocess.md)

    From a1380ca5d0d9ae9d2ef3eafbf2771177742231f2 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Mon, 7 Aug 2023 10:53:24 +0300 Subject: [PATCH 1784/1795] translate section 5.6 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index aa269fb16..6ccc92230 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -136,7 +136,7 @@   [5.3. האצילו כל מה שאפשר (לדוגמה gzip, SSL) לשירות נפרד `#strategic`](#-53-delegate-anything-possible-eg-gzip-ssl-to-a-reverse-proxy)
      [5.4. קיבוע תלויות](#-54-lock-dependencies)
      [5.5. הבטיחו את זמינות המערכת בעזרת הכלי המתאים](#-55-guard-process-uptime-using-the-right-tool)
    -  [5.6. Utilize all CPU cores](#-56-utilize-all-cpu-cores)
    +  [5.6. השתמשו בכל מעבדי ה-CPU](#-56-utilize-all-cpu-cores)
      [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
      [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
      [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
    @@ -872,13 +872,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.6. Utilize all CPU cores +## ![✔] 5.6. השתמשו בכל מעבדי ה-CPU -**אמ;לק:** At its basic form, a Node app runs on a single CPU core while all others are left idling. It’s your duty to replicate the Node process and utilize all CPUs. Most of the modern run-times platform (e.g., Kubernetes) allow replicating instances of the app but they won't verify that all cores are utilized - this is your duty. If the app is hosted on a bare server, it's also your duty to use some process replication solution (e.g. systemd) +**אמ;לק:** בתצורה הבסיסית שלה, מערכת מבוססת Node.js תרוץ על מעבד CPU אחד ושאר המעבדים ינוחו. מחובתכם לשכפל את התהליך ולנהל את המערכת ככה שתרוץ על כל המעבדים. רוב תשתיות הריצה החדשות (כמו קוברנטיס) מאפשרות לשכפל את התהליכים למספר מעבדים, אך הן לא מבטיחות להשתמש בכל המעבדים - זאת האחריות שלכם! אם המוצר מותקן על שרת פיזי, אז כחלק מאחריותכם אתם צריכים גם להשתמש בפתרונות שיבצעו את השכפול של התהליך (כמו systemd). -**אחרת:** Your app will likely utilize only 25% of its available resources(!) or even less. Note that a typical server has 4 CPU cores or more, naive deployment of Node.js utilizes only 1 (even using PaaS services like AWS beanstalk!) +**אחרת:** המוצר שלכם ינצל לכל היותר 25% מהמשאבים הזמינים(!). זכרו שלשרת רגיל יש 4 מעבדי CPU או יותר, והתקנה סטנדרטית של תהליך Node.js משתמשת רק במעבד אחד (גם שירותים בשיטת PaaS כמו AWS beanstalk!). -🔗 [**Read More: Utilize all CPU cores**](./sections/production/utilizecpu.md) +🔗 [**לקריאה נוספת: השתמשו בכל מעבדי ה-CPU**](./sections/production/utilizecpu.md)

    From ced052a9254195785270404eb8e47afe715360c9 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 8 Aug 2023 14:00:38 +0300 Subject: [PATCH 1785/1795] translate section 5.7 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 6ccc92230..197dbcab8 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -137,7 +137,7 @@   [5.4. קיבוע תלויות](#-54-lock-dependencies)
      [5.5. הבטיחו את זמינות המערכת בעזרת הכלי המתאים](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. השתמשו בכל מעבדי ה-CPU](#-56-utilize-all-cpu-cores)
    -  [5.7. Create a ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    +  [5.7. תיצרו ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
      [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
      [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
      [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
    @@ -882,13 +882,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.7. Create a ‘maintenance endpoint’ +## ![✔] 5.7. תיצרו ‘maintenance endpoint’ -**אמ;לק:** Expose a set of system-related information, like memory usage and REPL, etc in a secured API. Although it’s highly recommended to rely on standard and battle-tested tools, some valuable information and operations are easier done using code +**אמ;לק:** חישפו מידע רלוונטי על המערכת, למשל מצב הזיכרון ו -[REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop), באמצעות API מאובטח. על אף שמומלץ להישען על כלים יעודיים לשם כך, את חלק מהמידע והפעולות יותר פשוט לבדוק באמצעות כתיבת קוד. -**אחרת:** You’ll find that you’re performing many “diagnostic deploys” – shipping code to production only to extract some information for diagnostic purposes +**אחרת:** תגלו שאתם מבצעים הרבה “diagnostic deploys” – העלאת קוד לסביבת הייצור רק כדי להשיג עוד קצת מידע אבחנתי על המערכת. -🔗 [**Read More: Create a ‘maintenance endpoint’**](./sections/production/createmaintenanceendpoint.md) +🔗 [**לקריאה נוספת: יצירת ‘maintenance endpoint’**](./sections/production/createmaintenanceendpoint.md)

    From b9968ef79dc523c40b0d163a3b2f3ed701ad07cd Mon Sep 17 00:00:00 2001 From: hodbauer Date: Thu, 10 Aug 2023 12:38:08 +0300 Subject: [PATCH 1786/1795] translate section 5.8 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 197dbcab8..53c71ae9d 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -138,7 +138,7 @@   [5.5. הבטיחו את זמינות המערכת בעזרת הכלי המתאים](#-55-guard-process-uptime-using-the-right-tool)
      [5.6. השתמשו בכל מעבדי ה-CPU](#-56-utilize-all-cpu-cores)
      [5.7. תיצרו ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
    -  [5.8. Discover the unknowns using APM products `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
    +  [5.8. גלו את הלא ידוע בעזרת מוצרי APM `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
      [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
      [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
      [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
    @@ -892,13 +892,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.8. Discover the unknowns using APM products +## ![✔] 5.8. גלו את הלא ידוע בעזרת מוצרי APM -**אמ;לק:** Consider adding another safety layer to the production stack - APM. While the majority of symptoms and causes can be detected using traditional monitoring techniques, in a distributed system there is more than meets the eye. Application monitoring and performance products (a.k.a. APM) can auto-magically go beyond traditional monitoring and provide additional layer of discovery and developer-experience. For example, some APM products can highlight a transaction that loads too slow on the **end-user's side** while suggesting the root cause. APMs also provide more context for developers who try to troubleshoot a log error by showing what was the server busy with when the error occurred. To name a few example +**אמ;לק:** שיקלו הוספת שכבה נוספת של בטיחות למוצר שלכם - [APM](https://en.wikipedia.org/wiki/Application_performance_management) (Application monitoring and performance products). אמנם רוב הסממנים והגורמים יכולים להימצא על ידי טכניקות ניטור סטנדרטיות, אך במערכות מבוזרות יש עוד רבדים סמויים מן העין. ניטור מערכות ובדיקת ביצועים (או בקיצור APM) יכולים באופן קסום להוסיף שכבה נוספת של חוויית פיתוח מעבר למה שמספקים הכלים הסטנדרטיים. לדוגמה, ישנם כלי APM שיכולים להדגיש טרנזקציה שטוענת לאט מידי את **צד הלקוח** ולהציע מה הסיבה לכך. כלים אלו גם מספקים יותר הקשר לצוות הפיתוח שמנסים לחקור שגיאה וזאת על ידי הצגה של העומסים שהיו בשרת בזמן שחלה השגיאה. -**אחרת:** You might spend great effort on measuring API performance and downtimes, probably you’ll never be aware which is your slowest code parts under real-world scenario and how these affect the UX +**אחרת:** אתם משקיעים זמן ניכר במדידת ביצועי API ואי זמינות של המערכת, כנראה שלעולם לא תהיו מודעים לאילו חלקים בקוד הם האיטיים ביותר בזמן אמת ואיך זה משפיע על חווית המשתמש. -🔗 [**Read More: Discover errors and downtime using APM products**](./sections/production/apmproducts.md) +🔗 [**לקריאה נוספת: גילוי שגיאות וזמני השבתה בעזרת מוצרי APM**](./sections/production/apmproducts.md)

    From 1f0d733c5f8ef04f385a98d8e62fce405a8e544f Mon Sep 17 00:00:00 2001 From: hodbauer Date: Thu, 10 Aug 2023 12:44:26 +0300 Subject: [PATCH 1787/1795] translate section 5.9 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 53c71ae9d..169f5b70e 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -139,7 +139,7 @@   [5.6. השתמשו בכל מעבדי ה-CPU](#-56-utilize-all-cpu-cores)
      [5.7. תיצרו ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
      [5.8. גלו את הלא ידוע בעזרת מוצרי APM `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
    -  [5.9. Make your code production-ready](#-59-make-your-code-production-ready)
    +  [5.9. כתבו את הקוד מותאם להתקנה](#-59-make-your-code-production-ready)
      [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
      [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
      [5.12. Strive to be stateless `#strategic`](#-512-strive-to-be-stateless)
    @@ -902,13 +902,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.9. Make your code production-ready +## ![✔] 5.9. כתבו את הקוד מותאם להתקנה -**אמ;לק:** Code with the end in mind, plan for production from day 1. This sounds a bit vague so I’ve compiled a few development tips that are closely related to production maintenance (click 'Read More') +**אמ;לק:** קודדו כאשר התוצאה הסופית במחשבותיכם, התכוננו להתקנה בסביבת יצור כבר מהיום הראשון. זה אמנם נשמע קצת מעורפל ולכן בקישור ישנן מספר המלצות הקשורות לתמיכה במוצר שכבר הותקן. -**אחרת:** A world champion IT/DevOps guy won’t save a system that is badly written +**אחרת:** אלופי העולם של IT/DevOps לא ינסו להציל מערכת שכתובה גרוע. -🔗 [**Read More: Make your code production-ready**](./sections/production/productioncode.md) +🔗 [**לקריאה נוספת: כתבו את הקוד מותאם להתקנה**](./sections/production/productioncode.md)

    From d3541292ed761a3d9c0a852cb89c5709f937535e Mon Sep 17 00:00:00 2001 From: hodbauer Date: Thu, 10 Aug 2023 12:57:42 +0300 Subject: [PATCH 1788/1795] translate section 5.10 --- README.hebrew.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index 169f5b70e..ff79bb703 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -140,7 +140,7 @@   [5.7. תיצרו ‘maintenance endpoint’](#-57-create-a-maintenance-endpoint)
      [5.8. גלו את הלא ידוע בעזרת מוצרי APM `#advanced` `#updated`](#-58-discover-the-unknowns-using-apm-products)
      [5.9. כתבו את הקוד מותאם להתקנה](#-59-make-your-code-production-ready)
    -  [5.10. Measure and guard the memory usage `#advanced`](#-510-measure-and-guard-the-memory-usage)
    +  [5.10. מדדו ושימרו את ניצול הזיכרון `#advanced`](#-510-measure-and-guard-the-memory-usage)
      [5.11. Get your frontend assets out of Node](#-511-get-your-frontend-assets-out-of-node)
      [5.12. Strive to be stateless `#strategic`](#-512-strive-to-be-stateless)
      [5.13. Use tools that automatically detect vulnerabilities](#-513-use-tools-that-automatically-detect-vulnerabilities)
    @@ -912,13 +912,13 @@ API, הודעה חדשה נרשמת לתור, וקריאה לכלי צפיה ב

    -## ![✔] 5.10. Measure and guard the memory usage +## ![✔] 5.10. מדדו ושימרו את ניצול הזיכרון -**אמ;לק:** Node.js has controversial relationships with memory: the v8 engine has soft limits on memory usage (1.4GB) and there are known paths to leak memory in Node’s code – thus watching Node’s process memory is a must. In small apps, you may gauge memory periodically using shell commands but in medium-large apps consider baking your memory watch into a robust monitoring system +**אמ;לק:** ל-Node.js ישנה מערכת יחסים מורכבת עם ניהול הזיכרון: למנוע ה-v8 ישנם גבולות עדינים של צריכת זיכרון (1.4GB) וישנן דרכים ידועות איך לגרום לזליגת זיכרון בקוד של Node.js - ולכן מעקב אחר צריכת הזיכרון של תהליך Node.js הוא חובה. במוצרים קטנים, אפשר לאמוד את צריכת הזיכרון כל כמה זמן בעזרת פקודות shell, אבל במוצרים בינוניים-גדולים צריך לתעדף שימוש בכלים חזקים לניטור מצב הזיכרון. -**אחרת:** Your process memory might leak a hundred megabytes a day like how it happened at [Walmart](https://www.joyent.com/blog/walmart-node-js-memory-leak) +**אחרת:** זולגים לכם מאות MB כל יום מהתהליך כמו שקרה ב[וולמארט](https://www.joyent.com/blog/walmart-node-js-memory-leak) -🔗 [**Read More: Measure and guard the memory usage**](./sections/production/measurememory.md) +🔗 [**לקריאה נוספת: מדידה ושמירה על ניצול הזיכרון**](./sections/production/measurememory.md)

    From efdd0f21b0a13f02bc57e4fef8d5cfdfc0647224 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Wed, 16 Aug 2023 10:10:39 +0300 Subject: [PATCH 1789/1795] Update README.hebrew.md * add translator name * update content of header based on English readme --- README.hebrew.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.hebrew.md b/README.hebrew.md index ff79bb703..0a0758ccb 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -15,14 +15,11 @@
    [](https://twitter.com/nodepractices/) **עיקבו אחרינו בטוויטר!** [**@nodepractices**](https://twitter.com/nodepractices/) -
    - -לקריאה בשפות נוספות: [![CN](./assets/flags/CN.png)**סינית**](./README.chinese.md), [![FR](./assets/flags/FR.png)**צרפתית**](./README.french.md), [![BR](./assets/flags/BR.png)**פורטוגזית**](./README.brazilian-portuguese.md), [![RU](./assets/flags/RU.png)**רוסית**](./README.russian.md), [![PL](./assets/flags/PL.png)**פולנית**](./README.polish.md), [![JA](./assets/flags/JA.png)**יפנית**](./README.japanese.md), [![EU](./assets/flags/EU.png)**באסקית**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ספרדית**, ![HE](./assets/flags/HE.png)**עברית**, ![KR](./assets/flags/KR.png)**קוריאנית** ו ![TR](./assets/flags/TR.png)**טורקית** בתהליך! )](#translations) - +**:writing_hand: תורגם על ידי [הוד בואר](https://github.com/hodbauer)**
    -## 🚀 יש לנו [Node.js starter רשמי - Practica.js](https://github.com/practicajs/practica). השתמשו בזה כדי לייצר שלד חדש לפרוייקט שמבוסס על כל שיטות העבודה המומלצות כלולות בפנים. או רק כדי ללמוד על ידי דוגמאות קוד. +לקריאה בשפות נוספות: [![CN](./assets/flags/CN.png)**סינית**](./README.chinese.md), [![FR](./assets/flags/FR.png)**צרפתית**](./README.french.md), [![BR](./assets/flags/BR.png)**פורטוגזית**](./README.brazilian-portuguese.md), [![RU](./assets/flags/RU.png)**רוסית**](./README.russian.md), [![PL](./assets/flags/PL.png)**פולנית**](./README.polish.md), [![JA](./assets/flags/JA.png)**יפנית**](./README.japanese.md), [![EU](./assets/flags/EU.png)**באסקית**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ספרדית**, ![HE](./assets/flags/HE.png)**עברית**, ![KR](./assets/flags/KR.png)**קוריאנית** ו ![TR](./assets/flags/TR.png)**טורקית** בתהליך! )](#translations)
    @@ -48,6 +45,11 @@

    +# מאת יוני גולדברג + +### לימדו איתי: כיועץ, אני נפגש עם קבוצות מכל העולם במגוון פעולות כמו סדנאות ומעבר על קוד. 🎉 לאחרונה פרסמתי את [הקורס המתקדם לכתיבת בדיקות](https://testjavascript.com/) + +

    ## תוכן העניינים

    From c9547bf739f618c2f2d107ebc98d97afad6f27f5 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Wed, 16 Aug 2023 10:11:53 +0300 Subject: [PATCH 1790/1795] Update README.hebrew.md change "last update" ribbon --- README.hebrew.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.hebrew.md b/README.hebrew.md index 0a0758ccb..22d5ce0ba 100644 --- a/README.hebrew.md +++ b/README.hebrew.md @@ -9,7 +9,7 @@
    - 102 items Last update: April 19, 2023 Updated for Node 14.0.0 + 102 items Last update: August 16, 2023 Updated for Node 14.0.0

    From 360868bae133626c1213b39a7b2050d5a35dd0d3 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Sun, 20 Aug 2023 15:06:35 +0300 Subject: [PATCH 1791/1795] Update README.md add @hodbauer as translator --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7163b1eb..107abb928 100644 --- a/README.md +++ b/README.md @@ -1626,7 +1626,7 @@ All translations are contributed by the community. We will be happy to get any h ### Translations in progress - ![FR](./assets/flags/FR.png) [French](./README.french.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/129)) -- ![HE](./assets/flags/HE.png) Hebrew ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) +- ![HE](./assets/flags/HE.png) [Hebrew](./README.hebrew.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/156)) - ![KR](./assets/flags/KR.png) [Korean](README.korean.md) - Courtesy of [Sangbeom Han](https://github.com/uronly14me) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/94)) - ![ES](./assets/flags/ES.png) [Spanish](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.spanish.md) ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/95)) - ![TR](./assets/flags/TR.png) Turkish ([Discussion](https://github.com/goldbergyoni/nodebestpractices/issues/139)) @@ -1928,6 +1928,7 @@ Thanks goes to these wonderful people who have contributed to this repository! Scc
    Scc

    🌍 Mauro Accornero
    Mauro Accornero

    🖋 no-yan
    no-yan

    🖋 + hodbauer
    hodbauer

    🌍 From 8d825fc34de106faafd30f6aacb1c419a8e2a8a2 Mon Sep 17 00:00:00 2001 From: omahs <73983677+omahs@users.noreply.github.com> Date: Sun, 3 Sep 2023 22:51:05 +0200 Subject: [PATCH 1792/1795] fix typos --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a7163b1eb..5421c9e13 100644 --- a/README.md +++ b/README.md @@ -266,7 +266,7 @@ my-system │ ├─ data-access # DB calls w/o ORM ``` -**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the the logic code by other clients like testing code, scheduled jobs, message queues, etc +**Otherwise:** It's often seen that developer pass web objects like request/response to functions in the domain/logic layer - this violates the separation principle and makes it harder to access later the logic code by other clients like testing code, scheduled jobs, message queues, etc 🔗 [**Read More: layer your app**](./sections/projectstructre/createlayers.md) @@ -274,7 +274,7 @@ my-system ## ![✔] 1.3 Wrap common utilities as packages, consider publishing -**TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increases the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry +**TL;DR:** Place all reusable modules in a dedicated folder, e.g., "libraries", and underneath each module in its own folder, e.g., "/libraries/logger". Make the module an independent package with its own package.json file to increase the module encapsulation, and allows future publishing to a repository. In a Monorepo setup, modules can be consumed by 'npm linking' to their physical paths, using ts-paths or by publishing and installing from a package manager repository like the npm registry ```bash my-system @@ -346,7 +346,7 @@ my-system ### `📝 #updated` -**TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling .There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) +**TL;DR:** Some libraries throw errors as a string or as some custom type – this complicates the error handling logic and the interoperability between modules. Instead, create app error object/class that extends the built-in Error object and use it whenever rejecting, throwing or emitting an error. The app error should add useful imperative properties like the error name/code and isCatastrophic. By doing so, all errors have a unified structure and support better error handling. There is `no-throw-literal` ESLint rule that strictly checks that (although it has some [limitations](https://eslint.org/docs/rules/no-throw-literal) which can be solved when using TypeScript and setting the `@typescript-eslint/no-throw-literal` rule) **Otherwise:** When invoking some component, being uncertain which type of errors come in return – it makes proper error handling much harder. Even worse, using custom types to describe errors might lead to loss of critical error information like the stack trace! @@ -412,7 +412,7 @@ my-system ### `📝 #updated` -**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions an ensure that the error handler treat these properly (see code examples within the "read more" section) +**TL;DR:** Whether professional automated QA or plain manual developer testing – Ensure that your code not only satisfies positive scenarios but also handles and returns the right errors. On top of this, simulate deeper error flows like uncaught exceptions and ensure that the error handler treat these properly (see code examples within the "read more" section) **Otherwise:** Without testing, whether automatically or manually, you can’t rely on your code to return the right errors. Without meaningful errors – there’s no error handling @@ -904,7 +904,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.5. Guard process uptime using the right tool -**TL;DR:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitor an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location +**TL;DR:** The process must go on and get restarted upon failures. Modern runtime platforms like Docker-ized platforms (e.g. Kubernetes), and Serverless take care for this automatically. When the app is hosted on a bare metal server, one must take care for a process management tools like [systemd](https://systemd.io/). Avoid including a custom process management tool in a modern platform that monitors an app instance (e.g., Kubernetes) - doing so will hide failures from the infrastructure. When the underlying infrastructure is not aware of errors, it can't perform useful mitigation steps like re-placing the instance in a different location **Otherwise:** Running dozens of instances without a clear strategy and too many tools together (cluster management, docker, PM2) might lead to DevOps chaos @@ -986,7 +986,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.13. Use tools that automatically detect vulnerabilities -**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily be tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately +**TL;DR:** Even the most reputable dependencies such as Express have known vulnerabilities (from time to time) that can put a system at risk. This can be easily tamed using community and commercial tools that constantly check for vulnerabilities and warn (locally or at GitHub), some can even patch them immediately **Otherwise:** Keeping your code clean from vulnerabilities without dedicated tools will require you to constantly follow online publications about new threats. Quite tedious @@ -1046,7 +1046,7 @@ b. [Node.js testing - beyond the basics](https://github.com/testjavascript/nodej ## ![✔] 5.19. Install your packages with `npm ci` -**TL;DR:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hands, the command 'npm ci' will exit with error in case of mismatch between these files +**TL;DR:** Run `npm ci` to strictly do a clean install of your dependencies matching package.json and package-lock.json. Obviously production code must use the exact version of the packages that were used for testing. While package-lock.json file sets strict version for dependencies, in case of mismatch with the file package.json, the command 'npm install' will treat package.json as the source of truth. On the other hand, the command 'npm ci' will exit with error in case of mismatch between these files **Otherwise:** QA will thoroughly test the code and approve a version that will behave differently in production. Even worse, different servers in the same production cluster might run different code. From d0d349353b5cc54a7127b85f0c878f9bf252f413 Mon Sep 17 00:00:00 2001 From: monke <99099523+oxxxxxy@users.noreply.github.com> Date: Mon, 6 Nov 2023 13:28:37 +0300 Subject: [PATCH 1793/1795] fixed path to the checklist.png at test-five-outcomes.md --- sections/testingandquality/test-five-outcomes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sections/testingandquality/test-five-outcomes.md b/sections/testingandquality/test-five-outcomes.md index 450a11b72..d87386207 100644 --- a/sections/testingandquality/test-five-outcomes.md +++ b/sections/testingandquality/test-five-outcomes.md @@ -20,4 +20,4 @@ When planning your tests, consider covering the five typical flow's outputs. Whe ### Example: The backend testing checklist -![The backend testing checklist](../../assets/images/backend-testing-checklist.png "The backend testing checklist") +![The backend testing checklist](<../../assets/images/The backend testing checklist.png> "The backend testing checklist") From 6d4e0ca301c9b2b90ebc5651c5e99fed2e4c3e38 Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 12 Nov 2023 03:54:50 +1000 Subject: [PATCH 1794/1795] Update limitrequests.md Use ioredis package --- sections/security/limitrequests.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sections/security/limitrequests.md b/sections/security/limitrequests.md index 21482d050..0763a3b57 100644 --- a/sections/security/limitrequests.md +++ b/sections/security/limitrequests.md @@ -8,12 +8,10 @@ Rate limiting should be implemented in your application to protect a Node.js app ```javascript const http = require('http'); - const redis = require('redis'); + const IoRedis = require('ioredis'); const { RateLimiterRedis } = require('rate-limiter-flexible'); - const redisClient = redis.createClient({ - enable_offline_queue: false, - }); + const redisClient = new IoRedis({ enableOfflineQueue: false }); // Maximum 20 requests per second const rateLimiter = new RateLimiterRedis({ From d330711f12ca7e1cb80c91ff5064c1a10ab851c9 Mon Sep 17 00:00:00 2001 From: Yoni Goldberg Date: Wed, 7 Feb 2024 15:49:05 +0200 Subject: [PATCH 1795/1795] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 04a5a97fe..065d37585 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@
    - 102 items Last update: July 19, 2023 Updated for Node 19.0.0 + 102 items Last update: January 3rd, 2024 Updated for Node 22.0.0

    @@ -22,9 +22,9 @@ Read in a different language: [![CN](./assets/flags/CN.png)**CN**](./README.chin
    -# 🎊 2023 edition is here! +# 🎊 2024 edition is here! -- **🛰 Modernized to 2023**: Tons of text edits, new recommended libraries, and some new best practices +- **🛰 Modernized to 2024**: Tons of text edits, new recommended libraries, and some new best practices - **✨ Easily focus on new content**: Already visited before? Search for `#new` or `#updated` tags for new content only