From 93bdceaa9c637d97e6b1816125e81c84127ed70e Mon Sep 17 00:00:00 2001 From: forresst Date: Tue, 10 Dec 2019 13:51:28 +0100 Subject: [PATCH 001/877] 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
`. 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 761/877] 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 762/877] 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 763/877] 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 764/877] 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 765/877] 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 766/877] 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 767/877] 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 768/877] 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 zeNIQ|+;B=2Bgg;vp|3A*h%@#rO^${JF|5<1bwAHp4mM@P=XHxMaEXd}l#5-4Tsmvl zj9%yC@VmX~bTi3)hprPMW0^wrYiB;? zY$EZU#uvWU7aKQdVa@~XJ6H#2Kxy<}NmW}UKp;|c!B4r2CH{tH1k6D#>Z5rM%W1q& z?4^@xgH2lUd&8c{OsRxnB5V%uXv{jAt;y|H%5fVaoJg}Cc)dJC4dP^73o!heqW}Q} z%4M)1A_ODU4-V9q%+-S?X=7Y1=W{u1nor%t;~qC?lvqF;W-9*E%eB_?E;3md{x2ES zmn=$=0M8G0vS8s69Uy_FTJQEHN@p}QKpbt>{97e#$-af`Q2zafV2M8dQn`mbBb zn%X8N*n0?OdNQcNezbXrOo{ZIwzIyOLIQbxWQ!inNJvIj-aB-5M2Oh*f=0@i@5XG5 zbk!DVy`8vIL^tPiZ1|iG*j4)m=}+2b@)d+Ge=I`=wN&!umpUW43{w#mxr-_*?Lon# zN@`D!>D+d(O1aW(6FESp19Sa;kD=g-RBk)g?l3}AaqLNZIAj87uJ1#ak{rL!REZ|; z=@-Lna^((Lgo=HK#!I4yeYrji8WOlCR9Eo*%I3=m&J5gOR1^++o}P*P9-J^@aDVRAp7-o7;aS< zVS9qs*t9JT{q;1q?$e0+PN7@h@_LUPj_00vN6dv|9%oQR-(cRxy3N2;iHXy};z|Vs zAe~E5O1Q8k!v@{TNj}G!fPLH8L1nDgcOTZFapv6Cy^UNCV%mtA)k2GV-R8&jX6I%8 z=7%+bL6P|arM`IfE5wgD)n=m$ClhTJJJ5W^T+Gfss>shD9iBgaDFh~W#h@K8xLxX& z;lE};IjNE;bPkAo*Lo1`dAtO4_}Yf1h^Qu%*64^TmuFQj*)74)5jH{1j&Sb36T#Xt zgDEK*sn(-~EaiA0O(ga9sQytkgG#v+rAW zBhHil)BElgyI7-YYS7@I{&Z(4i|>eX{-Yc^i;m9-=_FbaUPDHrx&AzhS0JuL*QdO6 z^1ME$KHsLrMH8XLH>zbieq~SBNHY=O;2c7~ciI8J{YstAg;&i?^1Uf^qY!`4IE&eD z53^CX;&BwN5A)e_Dc4!G4TqAdX42dZWTCvIgZIWxxoBwm!b}U&(`3_l12Q{YSQq`I z?sUTDZSW1M?H1}05_zo~`+868FZZUpFudIff@_CRSVxrDd7dxEne>_|CJU6{hEg~< zvX2R#J5V}K*94v!V=(Ck!ICEFiLz0hpZ-udM@Qgs^pkn28CS%wQtvJI5I={8fF3q0 z_7cz_Ni(eLpk<(QllH=LW)!!IKcuGaNDvDP8J5(!u5-d`JScd{Ge>|2|2%d?uT{w;P$KO(~X>ag39SNUr z4TaB7YxB+Ebf|yr4coP2wdYnpIkI1TzAQ^fs~ol4hIyF+a?%L4(lb~swL(?Riq}*W zTNpHJU5zi)_c%RnM!GHj4Ca%x?PVkzd7n)v$)O9aosIMiPUI^XN>fGRRy%dppZpx= zbPI7Y{6P(FvPl{>+(j+2%%Lbd?!v~!D8Z;QA4%E2?1&W5e|mTuuL(f#kf3{fe%P)N z93zx!_YKXFO2B~*8=gV05U zz<{-^=?E_iMI+ggnp0`TITRaR_V^Q99I@v^!TQFabfQ5g_cGA^$~Q?+PCBc_LvA|G zpARd2$V$u8~)obg)7F|R5Iyn`33~Ch(#Y6QlL5J&FyNl zQ$@s>o-suth_XQ8nQV;2=bA#5N#ji2`!#!RmL2FOn|Zu~rtN$LD1MZ!-GTN>7G3JX>*Oy96)GZ}6#gFM-=TAdLE zX()E=eXm&W9*vmR9Gu7Jv|@MqUHC&YXu(&Cv0+Br&ZQGiN6uoqMwF0liYl>sZEg8loaT^kSgx_Mso>uy2F5iLnTOSG% z&m!ZKSsiI-G-|YEhOO^>b6|Y?l#K29yl?F48`-`5^u>&6ey)&(Py~YysAGHGW%W%W zTJN94V)_jG@Yq5ZG6r2L!sD63h?qX)%4L{;{EZ(*onsCBte4gjPM z|3jzhz@Rt5hG4+Oe!c2`>}jR}Ux67vUoi1&zSf#65k}`g+(4p#{KVEyjPNY z8lt8eDGr0LkJ+))+|2zoqh2&mIlM-#7%D{->3m;Vm;nLi-XAsf++1ETDKaOEXe_w) zYcA7i!q@A9VoRL-g{?Q=bo(VSDMNe)x`QGod1Vz@sSN_H-V{i>M4gI?by>uWmy+XC ziZr_&FY+ySeZNz=p2>MFRlN{h9kl8rRydl>ZXR}j+|t7u+%yohJ{u93BNU>}aJ33| z?0c?I{il{(scYUFqbour-67(=IXp_aZObk3!(lKaIg-WxcaO!a`RSOk5$2I1U1h)c zRRQpkM`;l~i#o~lJ{QNmwveu-zR{eg(ZQVtvuDNk63;o*Wx`G zD{w!kL=|HywRfs$us84~7l(a#qOV($RXjBin=fQFd#e&sQ(1QQ=SCgYF79mk!yHOjc z;LHc%-qxDg zZ9U>b8uWwcQz3&BzI{gdkU5D&ZNj#joEc9xw_$#T%Ljm^ke&^feRd&HwHPdpd9I^3@09sOx4=u-aDI#3xX| zI6n<%Hy{eg$KfbT6!}|I*Bx)A*v8j+A`cux_xb!CAHfJODWFCbHzuaN)pH&m)!;=& z#qBdHSdr!suiY;1l{?Ea7Arr@aVqML#4{G!1{OUjzwK`JM5#5b7OjTdoScFA_;M{^ z5k3G1c(d}ze>kQza&kOXGCwF)JGA>}6?XlrtSIOw8wTGSbc~YMC6Z$?R4lW#jgI^I zn&Q7Cq|oOzDJdI1JkByF<65Z7EPh+8ak<^}{(5kJGh0=S+mUi17_X38Zgi1)_gi~O zv(C0|qC&HdQB*OQO-wvB^~}Iy!^r;2{grH=M@Yx-Wrbw#|)ZVyPz*YGB4>oDW%(W>S_IkWKn2o=&cN z#q?fxuI$g6bUJ0Ttd-H)g=QC{YU|Nb3I6kU=MzaVq7H6Z`vW75foL)p(Wf=S8O6e$ zrrJfcV}9pf!=2n$`_94R43TzMt#NoCZuLk-%Pcrt@%ef?zbIshA~9Tz(Jm$E?oQ0< z+9T4zl13wo>&-4!^Pnjg0`Z(Z&2Sb1KfXI)$;^va)yR7_HRdZAqtAbRNHPN6=-**zv{go=d$Z%eWUUm*`?C~M(%t^cclc>vUxbd^ifpFcY9vt_W3nKFEgOF65 zi+Njo1M!cy_s(_ZAsNo#`ilE3cX1h7Y>YyBTL@@jaAY|ty4w3Zctc7K{Gj@vGTrad z-mY!$!?Yaip&BHF2(y32T0N(_^_3isL06^D99I<~`Rr|1xGDBWP`R;yyW(cIxq4TX z$E_rCG6iMm6^!7N0`S}IW`fUib99Wgx&kHN7*&{dXZd`jWHxNH4I(4{w}Lua+-o{j zdfWUgTttzF~C=UlpRi>`k7zvJE)dn35NHzdgwr7aMBty6vaWFBI zmKJWnM_=|VnCAIuP8oNL`qJJXQ>b=wDqcy*E-FE+m`7srk~?dy79$Ed=P8bq;$tf$ zzUKwUSF+FyZ28YOlfqUaDJZB`y@?AW9O;jF!`9yL_M37N4g z=UZ!yZ*nzWTmY`ic_6u&#@%+FzLL5)2$h>co{``^G3Mjd@hIqux1y6`b#-R7jR!Yr zF;UWFa)GySWF7UY5NeAqMbF&T%u}Fe=o?iXNv?6Rf64v)dWvfjNXyohRgw9~`RM^o z4eg5PKtg-$`%?g%f+~*$R;={MsurFyg@|b50laZDlm0!L*~16655+d82^%~S55?8w z>z1lbG|!42hrx*j;uOIV3;w?&tPol7eLGf$NI)yGrN$IOW=wU~O|>6IBjrBwV27nI zkRC*8V)G3bBR^cF91ogQH1^wI8!utPFlMAx(=LM!EDzxufcgcm+0^081eI>8jsZ^b zhr^(|)i@LpG>AiUYyQ zrsL++=J8tKCXv9gj>`%>tCI}DHc7snWz=q3NpCv(re!ALuYu*%Y*sg|FeMcvgblNw zg4%dUkHKJgnMbCJR4@yd_$!&iGO@^1=Tg!UT`DGfPXSo0@llSKdS>L!t?0d zje~b#2}?;17$5ES(G6n^`rKGL9FJSB)g$!5*N6J%b=_l_O9=e>?G%2uO439E9n>1- zWxVM-sp7H`PQg_`pf}tYvMfK{xMUo4PZff1Q}rGXO;RvwDg6O;bpFMIrE?Bxjuxx7 zLI)Cly_K1BG~42@Kie7J!O-k6IC3B}a9k%Y$a-jdMj*~=Ss{MLYRw#`^8<7$71-!4 z&p(?idr)0$A*U_L)1^jw4WV21xKA4qJL-Ell#Xp`qzT%6DE6>b*0GXo|B(7?vB?=) zv{Mn7b$H0WROB3ZzP;F>lqahuQ37cX3$k^&S1m@_FOq+$Nz2LS$`L-sS*phluFh%Kzjda&qW>R=OVvr&UBp)o0^>>z5Ecnw3p)@ERN zr!zD`C`fBK_pnJh$4Dklzm6+LnlmH(Q1}3Ex6AryxHh zEg17F?sdgbNNvV`wH{2gGjayOI!BS#PzNXWM^2_4^d60w+qXI0E6k3G(c zxFVFvtmmppGj!`Tl&h|S(+@ijC@5V#$^_pgEJ|aoc1W|%TmjCkocLsQP7g=ktbIL_ zHtMNt1;?VEvNfmFqs{)Tf##q}IxSHeAbMMA)MMM}?sIF6+hD0Xyhrv>`x61+JB@a$RM~QMR>{=(3GKudBEUgRYeHETd8b>6}#0T|?IA`Jvf? z)wjq+pEWjnsvY+^*Ei7Id7Y;{9#Sd#tY(I|Qvt7MD6?>J8AY2~qc}mh&Wo;a7Sjc9 zYt3V&tLQTzc1~x*gNqp6o`X2}g;-YbYYt&NY^g`-39hppAi$dYLPq})`2Iu&^y;yW zdS{2N;(RVW=rLKPk5>1-y)8YyY>@#lLv=iBn%?uL66Fp%kths{LT!CSpaLJ|k`wu82 zKu3^gqEP#MUu+Jh#hJmod}!v3 zmab=6A}06qppUs)KaLYje*P*B7J{#$i*2aE>Yhv#p1V@JWnWY5SRzYGn2+@oE*(?9I-{dowWG9@0)Q4Z0Q#uS4lz7osav7r@0h0 zf9$D8V{Us{guCq%vD-a&4~(B;I%}t)28)C;QdhD;=DI>KB)7#n&_w71cet`<75+O3#pHh1mHIV7FAI+b7aEtT#n4neNVphK*j~g2c>zLj&YG4 z_S}K@$tAAB$z_iu>a`kHz=*Q(V5yjpfYA5za06#kr5dJ;INUk~ok>16Qxli6vUa9f zRIDi?Jr}_9gY!g+)Z~ExYU)ilfp5>Ade2EAiP0O2zn49){b&xAu2a1z4J}7>b5N9E z?i?#eG3p5Y=(_4h3`v+kY2uA)tM?(dPLf6Ig7`4=%s6x542LB}szbCQ+^efM={ePV z%AySmrJ;RY0TfNNtTcU_Pd?V?+3b;vh7S94lOCzKm5&s0ik*dsn^B4DH#p=xKMNcl z24ebCY+J0Q&9sJ51fGnR;;)n?8G2mL`v}IULZ~O6qnlz!`0?M>;)2LbWceHq^p*@H z{F`_y<}{E`4#)y0Bf$qL$l3i8IZ7Bvy7P|+tmFNBj#%Nid^&F#Aorc8Up@Tx5B9t9 zr9{1SC_0-w9A}fXY&9--Ju{PxfU^#gYGhkT@5>7<-W1GIoP&H0H!zcQ0&x8@ym2!b zk!ZlwT=@FmWa7@!SlLcczipqjGXfpl5-74pM8ws|4rKG3k9AD?&c%6wsZJm1c9EL^ zFNa0+_(EZwK3F@r8kNM*Be^rr1v}jb@#UXd^+|D8z7s`oV=pE;oU!%ksgg z%5@%c21#E$i+&`1PNHTSR<~aj9wd2s+RoTw@T%43o2CIk^EVBywlGOX^@b*{VH+$z zmYJpq!m=@ZWAO?3);kn!b>J7=N3`qj3h9O(c*?(Xmb5(dL!|4d<5SiITe(^jvFbbI zjm!~japPh{BJlj7;77?G8oyjT z+%yy9ju>eePMLFyX<9;JDJ(5n*Hha}&~SGxh}hF2T3KuCnhcRMHIqGR%aTX$oFgW6 z=P-YSqonBQ7y!bXoIk%%o#egcOz} zo!OAWdg+uj+8(J%hN8)XR`fDlKrdNZI-4m`y4RNL!!qJ?8=|*28gvJuI-3GMYXVe7heF;6n1 zj|p)0$zLU0ISaAm4=U@}50R{UXavK|13@iIZui<946#c0n|zrEkIJ)qYK0DI-MBhN z5rllm(8>?BNE}FU;4{+zSq-^5gB43Na=2{Ybt6AlgW*4f$P3(0M%<`outI_3i)B4r?AQtz1 z)jFF|lF3jy5`T>#k^_Co-k0rEMn-O{6uI1+?a(ru)^3+JLzAvnzbLg7HEq{`XyWIX z*D{#63{q=fu7atFI)n|xUExY42QZlD2a8FmRyPx#^eejbgSsF4xK?6mZm+}_y1kok zSlzC6zWBLfo%Fpr-x>~5ZeUUJxSV9nRhc9W?~jg+HeZZtzs}|hf1@u~kiI`z#m7^i zqj%&_B&7(9k1ZDvFhS3i9xBU|Ec0qecS_-KWRi!B1qSD2hapIDdXjp<1%7BiIBvbp zj82^#DvmduFB3I#?vh5cbx;7K%XZ?!LW?@|?N8fcrt*Y>a7UQ(o|+FTpSXq5bvd6B zKD=Y%<$W(va{4{U`^X7YBzAC|J>J=Gei)Xn)QD-vS;0H`C9VVVfV}KJvWUZbU>{Rp zkbBjMFdg_NiC=)=O*E15U>K^@>TQbN+VNeQ^Afarec58B>$GlU`4GgL;XIK)8sGKK z`&#xmOS|9ty2un$_!+aKbDCNrnKAfS+k27Y=IEX{e5a*)clG$$rH@;vfpK;)U4qB3 z8RH>>#(;VqTDg!1&GWSJq3PD_EyxKp;SKGH5$KC1NL$@MDm|S_5IwQ z8U7WX&7!1e3t}EIu4=;45vN^y%l(}8Q{+7{B@GR4W9lt`6I*l?bdc^>T?x8L>&%v# zE<%_YS%W4FA?ia9H-_FU^S{=BkGyf4G%c;zyzuI#^MB0MlB^}t=4xd4h&WkI`er*& z1k4C1M(7sNAdeBVRolsj>v4SdBlf>Yg>0y{3+4wUtk%0_;*ILY9F+0bB;EYH9?YB5_~&30twjY_(lgQhd?|0tWSIiC||fJ<5W2SzmMio8~HYa4c_ui-sw6 z9Xa6kM7G@<@)Sr@V?Y%LEqCGP&rbWW<0mngSe}*+W<7aBms7ITZ(>rT3z2Gb(vl8C zVRL>kr8a3Jz8~oLxEl`$_e0u;JnPp#vN&V~bdvBob`XPOa>wl@*NTpKF6ovHIp`tY zl3xu>Ex2V0XG#e0@p}{k^0637)p;MWBocCjLoh&3#fqD(6TbZtw922x$%IduT%J9z zSHiiLI5}Cree{q-KBh3HN~Hu_MN(sbNe$fhF6wBHKCTd!Dg1h9Isw9?a*bWzRZ7Mfy`ILg5e;slxWieAu`gFF_@o`IGaohQ3Pmtl^WL_njz5i=VS^K5V-&9j?m$ruaa_wk);?eleH;61$`&}99(V6nb& z4Npy%th~)@sbL+8J`I8rMU5^}YTEcpZLy-S&YiR&aKw!nvaB~ttUu35yzfhd{=@*v zrPk~%b#EZ1NHiqw`3Zhxd4@Er>yZK$1YEiA%x?k9$1+!1YEisjY|7=$V`C%Aaxldf z)gTL{*t!o*neR5z5(jb0X_Jwb4m;6(O*1^-6GyvEr3uiaAHbos&gCZ%$Q$ci85UKt zYS)U;rYUdFMv5t!yPSAFHJFNHaZWhcWgZpink0gDb{JI;St`bNQfwPXS!ar>eIAU3 zTyn^?Yusvd!-%H+v}c4-tH_)mX7_s(v+sA55zV7U%z0yCZO|mbG;*LDSEqn;=V^g6 z+`eYllxB6AUcTm8{!odzus*=8w&^sW*)YCO;x1Ztu;iD;WbsusZMygEqvxS+8IF1t zW}w_qzQnRBT&0?k$vqTt1kan*bGc&c2bq@RCp6)~lW)*jWe3kGJ-U!?47Z@MeE|^B zfO@jy^FUaANSw40_cu%w8taCLhtzZV><{zTYoXuHhqykDnf0<`kKuIJnvw^j*&7r? z&#$cL-=)beN3Wx8?_L0U&YOORuF>rzit+aaC0&S*i7!Ya8uB^rEsVxCH=MuLsIwtF zXX%bw)+ge_TKx9t7)YCjtH*cR^sw^!+K(&Cl^hz8e9d#7()XLGS|V=C7>8InZk*4iORO0C!7i{Sd?e{E+$9Ty`RV(TN0zzZyBdY8Kq60sO49q# z=LbF$@SE?z=_!p_&{~Z_DTL=jl<8NGLY`hu09PG+xIx5zjjuv8ld`^!CX< zPKgGZXeZ61bn~(7V13~c3@1wz42q&YFET!%B?W7N?fCN}33Ra|IsV_G$rRji1L_ar*2rCH$vt&59xkH*R9$|RYB%8&>cn*~iu!^s zfV7|<<+J;fKSC%teATx`sU;MI>y71w)toGX38*nW%fU>Y_@gt${_{P^8?*G;=2f}f z4NF!%TM;)+F&h^{1NT-^9?;|1Z~+GiCD1< zKi^jA`i9NOif#WQ&Ex#=IxtIjI3{Tgh2Le`@~X=N~{VX}uF)Fj3v{`H0W< zYpX&~5A4S-eEXS6MO#H0KK=anw2X{{!Xrt*X_xCmzF-yHJFaTfmABPt#Zn0O+Me2E z@C$N#F*eymE8)2GC37CGu-C^%-GgM~%Z~3)33o|;$9%$tgQT_{W)>Gf1sHP9SO9pqH8xK-IJ@EDKNVr@eCR9sWUdj? z@4b{?UteB5e&jZwnRMs1Lkh(=XHMtkREp3e+}dw^62fbW;#;NLkrD=yLp@=B! zsm(xsSlLhoC{Ef7vj5Qn3a-w9hbfq|60J3Ab`^{H3m}Gn11lSr?ky_qlOHfF^cTaN z+5>-bG_lmt=u(*{0 zQ2Y8-y?5tcR2bv<#Ku;2&*l~)oI3CZQ2hlzqe{QPx}_Z)m;OOWleCH1N=SyuMJ4bS z|5Xhe0hIxZTre#EcU0WudZriX=p0$wVX+aDP+MVNbif8=4~zZncloknWl=~e;;Xv& z_2dus3&?ln1v9q7GRgB|1Rjjo8`hmcQxT8a!lkgxKYt)EB;_yY?eeI}+kZ&#Va(+Ki?@rCBt(=&o2?Vvw4Nc?nqev=YIkG9l}ajTM`@1R(O3q_dl>=DDL^i zIZW!kdmjHd2gzr>Co+XarAURNgZBm9Z}!Ib#ctn#;$gMFcF&-4yRK#9DnTK>|1Uz? zBX!cg)>u1V=KS-N0A7p&SJxXP=tZxCIX`8xQWXk^OPbdH0U* z$9unOjOwbfx~pbY_v*FgEQo(dcehZDZWr#1&>MA?cKwAq(Q>W6Q(3jzFOK4pl4(1V z|8ejK#J|b^-zWRb5BukXKb$G#xPrK*bN#35()@DEU!>LbEgZ$F^MhoZW%_@*{r^KQ z6dT$>uWf`TU^?zaF_wA=Sw4F(Jn0k{4gF>MMSp1+%W0!IG@ z9`=9YlsH7&kOrSu=I1Z*-WI%#D=1s0Jy0fhe=F%kPo zAXpByoGfenKeYtcpFch7T~NRMmHNNh4F3_bphx_Vo&sGFQ@2=M%WG*2Qv$c5smyLS|hc_ClduWG;0M+d?gnJUO+_i8gp-RkZBz zHcMSx`U4~Q;E;GAS(FZB^P=zUKKc6(y+p(CuRgh79m-Adrh8}JI=z=%i8^>BKE{y` zOPf8s9eh1zLIL^#`24695t=s9@=jfnfitKp#}%%TpRPZxA5Hb=Dk3DQ)swyRRNV73 zZB$kmnLZlW@*`+jajUR_4*!bgFns~~Yu!?w+Il$rqaSFTx_iBA3HVW1B1ee-F ze4}(WaIz%m%r)AW0li&FelNDwxcYRQ^~m<10@OX)*dVs><&$4j(AoH@p5roOww10=HWs7fi+W>tJ`kW#($X3kxv&#@?urDj{GDE~UPrfp;4? zBHQWXY};d6dYJ+<;Dbl#@LRubGF#Zb@R~l4435bwNK$r~HNFmOe@k7byN@fRP{F)Z z>L=Q}+QhDmH6YHv;T9on!>QQBu00u9pd-j^thO;I<2bN9=8MWaR8}RvOL-+BUGT zwj$@OZc$FL{4_zS%`WTzO~H`%nR=3g4#H&r^kC`dvM=8K5U5>}9(dVaP>ZCNL;NGD z>(E*Vau8r}chcNmCe84UjdUL&VIGa2mXQ(`J7r%#RagJdg8Mu4cEc2xh1Ny1;00mq z1tPWH*!SPD30tEDdw6@hqu}DEF3&bUn7ht*>6V0fLX6c28XT4=Pda=D<_S;rc2eDy z<~{Oafvu3PItM!@As1~Fw5b)%&_){1M-4yWAG|*{z{u}wUFnsk-DkTVPY;`9SNEZ` zUiVXnAx`6hvwF1*2wLJ0%r?qTLVj(O^i5-Eay>& z2e-!Ixh48+PGF`feIV+3yd_ny2g;Dl4S7bqp(~xKyA}yo6>*-i+XaDr7I)b0j}oyo zm$4ZNBwsv*q1U~%*;1|H;P&ACEx+(;TnQa^0P4dKk2uNM;BMtY!<`#WO9hs^r4?u;{5g}uUnD)r zL0sBQaWRUuqS=q-l}&3ZH<}>kueq``@yX)%Z*DpQ@iqAomCd+j!w*=IHM(d^UmDo; zlDx5r#t%(Tx(`+|Q=>xHS`!PkZV|LhUpL<9w@Yb@(P{=_8JZhxHjQ!>E8^A==OSfA zgIk-eO-6##R(v?VaGz*P!3FbN&gv=X=fna1Pj-hk2r_%Um_2A^iu5)aD|W!rm0o1) znx7N=UY`T&Mn$d?MMZP-V)J6GOxJR0bUN-o%`^U$td+cYv_H88WcIpz^*9p=Z>hni zcGwQu50$R)@%j2UPx0HSPZ~N!<0uqU4nIkAhU3uI6yIWP9ywS5kD*K3FqBk92qEupfOh z*ysszQ!sKz)|JsV8({Pe&7WpRZUw$0Sq;*WDb+8DUdF*%YS8Uo UoDs20?pE*~LaoC^cgE zleak_Q=!J4JvMls*IvKNy$^0UyZ3~d{YEcV#Gga*`zscEA)JuEY3jo(2Z&^z+tFQ( zB7ydRp3gv)5oramB?INlRy0(c_iGAv`0<49iikf2>heXLqdO(DdG@H2k>wr+?{~?E zww$Axp*!!%2CE}g3z|jo^Y&o&gsFf9jt!CQ~-28!8jIL0MknjoDoKv#g< zu!e$eOVm@I1Yb%@G?gH57k2ex!c+SYmq0g@sAZR09gixv!+~4jZ?ha#Yeh1Vu0xSkyJ`k>XM1kVafuu1AF0~cwcI3}U`eLSdERD{l zgwYYol93o6RnGklmrdL|KIGYhH2Jp~2ANcjY``;$Nhl8$yqjHtwno2v$uhZ%a z^X?T$FYWMM+6z1I_1_9V7^|D#k04%Vn1aQXXr%j5q*vcpzgVCDT?d&ND=-=CkDi<7$^Qp_zcojm<}j3^ZaXl3w;%uSSlVoo5%FU*=ipgbbwcDbW0PP}-!7yF(Jn z&m^r3D0iD4%_hp7c2jhHquxppslFm*{aYRC-B;!7`4*NFvOhi4%{r+p(fs9p)t*V0 z0}=AJ&s)QX6OTV(y~RtmlR_|I#fkJ%?jOG_LXc2Pz%I*F5abZ(_w&dYg7|et&BrYF zOVTY~50s3Gt!F0I302Bfcv;kY*1Xm}GfLSm1we*mgM_H&sh=~n3 z@i^0&Mq6e?2hqMJP<8vUp#FJ7ybRzcIX!y+iPo1D;_s~*DbNc~CHf9a1+-KN?|5FB z$_~F2eagmG>QK(THVhw^5FJ#uQz93lC3>-X`|0^ENFw*cZ)_tW ztDrXwjn-aDFU(7$w9)o%9WwMa^dM^tUvkdQ)o+W}%YVhfekNYZY?$~g>(02F#|{4B z2Fbqs&M*^bf`_^0DT&pZ*j#@7Z%rS9aahi4GNkVWis^Vs)NQ2fp_^oX%~NKx;_aim zgV*0`3l?|fjH=8F>(GCS-7do@=%T0a4ploZllB)yHs{he}1>r4Aj{I|YLV1&ypC_rl zWI6ViLz?s9_gKlrYBK#y*1|;)&S{~e87=CCd@(>buF|O2Tb|*I!0>}b>lKsUkZA|^eA{4mZ zv6{7rkVUOYQ%U+}Wrgl&LG=#}$LeiEBrl@n-u!CLccJiau+{wHpsd8ftGpRkS$t+t zOAOhlC<5<9M&16MSP0tIV5A($+{0md#dZ~9ao}2HT-N4nm?Ub)dYc)Yzw4?Xuc^7T zvfHm;fGwk46Lq^1-_tKv?2PDJrD%>0i(eKVY6n*TW`O>R-N!1;tP5g@&iok)n-K~j zIysQvx&cp(L^vf3W&U9XSaW)Io*U$nvk2E7nLjP}hwr#MT=b4Vxf|hwg(18KmMh8! zh1zM#SK#*5<7E%F6IaWd`35^XT2h}^h89{xgv21ADWqsT5V>mWn%wAEYr!VasD8rh zbUUWu@EeaFH?qw9qG~3_DjMYR&hq)eYIRd}@9mM@%7e^+ts+3Ax3a5|;V9tsepQR@ zjD=H=>LzW5?P7+peJ`net{J|K6%TK?Cjn2p2EoyOfLmP01OLS6h3~?K#K|OCu#hCc z&y`P_$uXgvwMP6r`@F|6wVnix%Jq82kj!hab_wH z60sbag5k~Dn;E?6(`9_)S8JwX0mC?`X{5_ww`E$3^I}R@XOjiQJILh1v0j+AGyk#C zJISl>8$312k6;0W?wyl zJy@K&_a6SRPVf^K=a%G-4F@I}gy_OaZ}x8tp2@Kw6?>Ju{Kh6_cyD>u)vE)x#?=_5 zLiBkkoiRbAba+M6SAmwsM;QTN!dk@2BqW*+o+tb|ELpGVZx>`BHmr+%XQb@;tAkGj z@6l!g9nZx_l><6FHd4WlgnP?*)}$ ztTlLC(h#)S=w^s2H*OC%uQbfvhdes9!Q!>^os~zzn#b&Jl+3hhf>H?Ap{@@>n`Of;1aAM{OKiLT6|hAy^=?k9#^Upt)o7ioZ+cax@u@oD+QG;{|2@FykaRTI zHtYCxKc>ACs_&j(mXIj7aibitHi)0;Vk>%d8ABZua1W^c(|!R2>b(B@a#rty5x%ju zmbu{4R?Cc>PeR5x`PPORrZd{u>kLfKR3GP4q*Chcn*I#Bj-*RF&r&Zxo;+xrTjzD! zv)YK`U0g<>gM>452wyL{E*Ca7+DuAq+e%!6>r-Zb4Kv$VYH;DnC}|HH7j)n;BQF71 zz|+r|^$-z^9DP(VqUz{juivL_oBrsIrw!PH>BLgQ@=inN)0@}qxTQ>ZN)pvHLEVuR z^Pe2nfGn@tyzB)J8-;n%C0N5J|BvY<~fKbm0q#1!`=RRlE>rg$4RL`F^F1Ik^P#p$3_o2rnbl|uy zc^iI?@5L4;;u~X_x_6m5XZX=3m=7}2kh3u5#Qupwn&FSUR(<(Xf1^B5 z6Nrb~OwEZM5ld^yR{isr}fo1mG#>3NH(xX%nzQDUwPXO}iOs zqEeMFW6XOwlH;d;Y~n3rvw8c`aU_*~PiooesFD}?xIGL+*;uMU=;Yw6#R{T2dXpPI zmKq;CgGCOaYFvjhAQl=ToqH$l?F4Fl@9W06@6Jf(eb71ly=wnz6HY}Be8xnJzHi(Nb9%~bB2q! z(sT~{!?Op5ivzQ68#5v2EBc!;k$)Mi9_`eb-sJ%5N3JSTD&dNlK<7UThD;7A zB3@irqZx1EtiX0g!gPguK|>T7SxKYu&1R|Sgm2SpOBlVBQTY#zhGAD%ttOYx5!|QI z)hxQ4bU`z0j*>EXDp?%O@rnX>_o z8@x7)b6R`l?@|^PxGp(aJka@MENPxpDk8LBvMMX75u8;WeF$xt#&Che{`H`=x!V)@ zhW$yhtCQ-O*-CjCw~I9bhp~8_-T`l!L-U~_2^){m+ja#eA;XR}arV|((|$`wk4-Qe z@W3ijrA??RrM9pJfl_YC>FdP#uLOr?q02$9ORL3Ji#;#Z51hT{9pno|QsGqLWCs%8 zK5}v}mBINK>nwyh&5pQSo-^INs;7!PV3dnL!IsoS4lTzH?U_RD24+C`~&(I85(@suHUWf-^pam5a%pjabTXl++=tya_W7qP zhps(+3j(9xPzC;(dw-&sjBT6|V`2-@sEJmmY$HaCG-LFhybpZNL=8#wQ5V8v(p%>Q z%1MVEhKe|s8m>*4vJI^_HTOE0EzHF%Ww1FUkxSY?tJw;yLMn)<&ki{qE?>jDIWz1hJMOM<(*P@bT4AWG8f5z$kkA8dwUwAeb5V9{=yI(vY%y1{nl z;!aD$Iv)V);jc{$5lRpLsx^=_7UJ8=oIa%=n|4aO7ZX*Y(mIObb@i}Sq2I6htJXNd z;Tmrz3YBN1T6X8@0~K>C*;q>Rv6MI_BKz{|Q$yHSw%r+MPdLB?iLKELA-&L}aG8!w z_P&YRTs~GxOtU=(ZV?^xjzjCFTpo$mp;k2Guo1IFG-dnP{E(*v4-coTP}9Np zp-9@=YkM-FW?7T~CzAJpnF<9t5-%AzVI=L#oGl+9n~oUb;6*)^zfP+@FMF}z<>X&> zto<>^1}=Og$~=2?_~G9I4=^#(3 z`?-pZ*WCdF~Z07QvZ2b7F?g308x`H|y&eSIJ4 zRdvAE*pDH9VKzEh@tfj=NUXblifXwx79n!Xv%|^@)h+o0300-vBkpenN*L?$rKCPX zD|&6@$`W1I(VzAIh{+!KIgq+WU(xm`5kReNka!u#PF!A*E1@mFdy2dIQ$05;;EmH{eqsv5L6JXO57lTg!{a78!Dh!`*4JJu5I z6mb5YO|0c}ou6`(()|8G^wk!R_7?Gtdv}|N=Hl1OJ#DYSLmNW>PdPwY?8tlCxcJ=S z!Q-%rX6T?jI`voIz!08vU#If=&jS9uAA3T*CI7Emy>x$jz=Y(8l!jQn{2G-XcSpN` zO>;}xv-iIiOFrE`B=8drbu!$31QG~W6K|SG((LjuuawV2b?cp^C8-5Bn@ejC#v7Vk z5b#`gK;!T`JvG~AF0{>iZ9Kwh{aqHL4u%7H+~wA6-SpdxPR;Y4GL!J{k=>U0Z9WVt z+|u3P_f10{&%2}+M>Qzxxz?!K7s zp=U|s4Ik_CSjKj;Dt-?;ZV#)2c`2$*7csbB|J{33%XFWvz~nd-g>Pir*71wSjDDOy z0A;S&s`n>boU6Y)klJ#!83{qTU?~3JYa>+_&!;3f;cE5+gyYOK zi5%jERmz^$^>m@D48iif>c(n7DEon@`#x)C(iSevagVi_2B0@i{0&1BXK8Q7>{9v$ zLrO^eB5PBJ?KKd;S$KNpk(U@p?hBbWBcd-;43?l!Z&|bG(GfREHPCliESJBRWKuf` z!flo~nWRA1#o0rcPQFHd=36%0I6aFk9`J(S(H@zj zfLXfV4l);9c`JoSo^8Ni37|O|`|%1s=c}2C7Ly|UH&~@X+g$dh!(V{AdB+4kk+#4(=eTu@G!<@k zo!h-5tajezWun;KjT?4?{#9jzp6a0@50&)gk|L>^HugoNva21K zx9OK|6G^+jReL8}@#kr?HmKjw%$G^OeaQ;3BwmmA7JbDNb&~J@)n@P|`4@Hwu`|_P zLHs@)pT~hstPWrdy~3(sB^c1h{O!BUuAqV|i%T2Vvu~_{yvRcZyspe&(|u)E=M(7f z>PxyoC->_t((;QXe`u<^q`=G(Bg+b znTd)wCa7%Yg^IpS5r&o3jvEd}TeLW84k-L1juh)MBEf)6D?hy~P+=_p^XT^1mO&2K z$1U|2{fNh&$Xr!17fbsZyv;HnpExY%^dx3lwO`DwCvy97ZTUJ<-;9N`)~yb=-#Tf7 zptq8=k>|YKCI_A-YgeBDQ!-u`Z9+{Hu@Zaoj>GEOZJhS@Z$$P6ILe~#g#Qpp0Jp;! zneP`!Pb_I?9urGakLwqM-1kUY#83_yu4GE75Pu5#PZmXP^>+XT?V7PGNdvd#JU+fVnbRlW@8!zPZ0na$wJzQ}AED*BrG)d0ud&$~bHO+DP3yipM2R%K% zy`iEcUS5^ya8o(dw7nxqg#Fn}@UtFcSAHaE1#ao5G&ZDcKr@dOd zD}>e4w0eK2y6y@pk67h#)>LrE=+0y#I48>MWmPWnJ@BCyrnh!a79v4`m~3*{lagHV z{d-d<(>S-I+}R3X?blQClb%aQm%(`222IDtK|z2V@o1Dhv8R6Bk8~Q{yee#|@1>H_ zJ)ViMh(AAV1>%ND()3gE%)AF+l5MSXyqvFlH#c)O7hThL-(vXLt_Qr1Mvc$8H+%8> z-7m~sCaiZCJ##;^=M&SKcf5HOY;=?9MNquLpzeInWHsyxzYRvjWjb10^G@jh@N;ZgpMm6R{%=B_2h3;P=8! zzgswMei{ZD?~xV!{U)=A-Fn0FVS5<0i;QLQId`4qx%^6zd>JpwejLwq&$%DIhdzCrP5@ zv&KT(AX0*xz0>Y#&gi87y{lR0ab=q~XBhj#YI++o7Vsdb&8vF!z84Vc@d?4?^A{Nf z$%e(IgNr`LN#^D#!-Q>V%B)H7b){vUp?IqtPt{Cp z5~fqh3R9i>w`Eh%Nh^*1QcSg~GR`0Ui47~_tR61iuR40FPz_C?>eiBTs|X30W1yA! zR~i>OBNWbT`>;epFhy%&elAQVG5Bm;SQdrLhV|KJ3%rj8)eAmXCQ<(*QO>Jy=E=3x z@e!`w7gLe(#!G*@2;qQL1q8&l^gX{iuJD&s-oT0!;@cO1t5L?95O`=vaSOC#eJ^c? zo{bkvg0zm|9&jwbeJ0UAHW;0T9Lzj16c)qfw^5^&4lfPW1Q8R^ZvS^0{BO8t_7@`> z7b=O7*6QIB?hr$To9p{{6Ea>-%2_9N9JUlpWBq{#%!8p0-aE`&Cx9Mh3=v9Zna2*1 z%%JTp=ajD)-(%E_<%-lkLVA7&xHE5A>+g$u`POTv_>YXws8npDpXtY)dDMflB`$ZL zTD9)xv<~SB#!w%*7lKw0oCDY}o;w0Qp<0lrajCxXhKdv=ep1M)plL zV}IaH-`fcg7-6Pa_ShXdn;))ahmQS3#YO__SS9Tw5DL(hHktUG=rDFT`c8P`f4Mgl z1Bu}EffqZBE-j7xW+1Q{;P+;_VTvFUFyyG&R!HkFSU3l8p-8w4(TQvrXT4 z*)P|9bGvy=NL+>zxCV}imiRHCCG_55Sa=Up4j1_|7Bzi_^|KVVaxxsB4^pUw%f9^7 zbObgxBy&iybxxmraL|I|KeyHYne9JC!w$xkMak%C4xDJs29)$q3=>%V{}n=1{lWRx z22U*ir|ExsAdy1h-r>HYyCD2Ot^QkF#ePEa$p>WJ8CLEQ8~OT>)kR|QiDq zjb5#hxf+*LdNeO2*eemhLep@OnxLYWrjAGs;{Yc}hD+Aq}U_b}9w%`!xdoC^( zw3;>wODb>SHdhD|4B995!0S&FGCR0PKI()P(IyVFgKPp>Xv6e%Lbvr^uYr%n4xMCG z40A&2VImX$1x(@U_^x`$_;>KJEXSS}O?=N;+s<%-uMfP}Z!iw#NKB&gfAo0NC71~% zw047LK0e1hYsJ=HV_Mkkbett_(V-4mZ=F>fMdXNOE)UJ^?ZkpwYSDIQ+z?wP3R9>bScSL!xtM zmNG<`r6cF=J9r;yO*dyBc3{o)UBFW#Os$$H+?HsO*oAOTqPeIqmcSy3FWIsgaHsCK z&BVFPTR4k>Fo5vabco6jp2;oHtTb$lSdXwT_oerARx<#XMmxtgh6-P;S8%a_r~Y_^Kn;pA!k29e?Cc6$F&&D7kEXz4hN z0fw-ZPD_5wGa0kELDL=%&yuq@ESu=*c;uwJYmn^jXag%Ys0UVkWRN^@12!+ebJBW zZw3r-dUcf!A-d}!3r;;z-z}k|FScWeDVG=BQRtKmrT2a!4sdA^@zje~`PaNwZcn1i z<{j`kH^DXLc$hi_L!TdQj}qAOUogZ=o_477sV~5|x{>m1>BZieymYZc33vMB^3DEi zUvDewA3-P$I^}a|hePZF7OCVyz9tu8l#gj~+bk%Ra*;k%E82;TLj0$X2)st=2(>AP zH;iMFvG4#T%RdX-4Qe*kV(op&NaRSm@*`=Rx1Z!;-0As!pjsuMhDLeJN{j`8(su^T z15uMGvzZc!I_;$T+P_u~%L)7~BLK2FaR@w?2?Z}KBd&oczkyC-i%T$AS9r$e>;r;S z4FInQI^J?A5TaL@*IAl%bVTl}*bY^>{tZGZ;%%mM(ei1nz8%o6XHG!g60@gaG=Fd= zUo;98R&352qyFtx+pENAWihj;QXnkki11%4ZWQuMf@s_gu>><`KC?AeA(#Mk9| zw4ymQEaJ*bf%rgYQup{W12qo9HR~NYn>dR3QrFWpq&G5V z%Rb|<>GH0z_hx?Ps;4}mI4D%8wV3Rv;DtS)jzocOd5?sULP0uHtfI2R$yi~ zvw{KxdhvLL2$xRh?zUW?28(bj7R*Uty~JW$9KUZ>9Cw7s7#ey?nDGSM*#dEXRbGmdm<*^tSW+~=Un!6Uw4)f<}8v-X(5i| z;dNNJqWv?E8ig4mBormnd=HBW*SBcd-Hs{Qg7rINNoriFeNki(GJaCz`F~XDzxIas z^e0F}LT+E)e-?C8kBGy$F31T%Qp%khc470MhZp$fe}sOYi;#DEB@|utpAM!~Ak-ic z4CYjH+hL;d6+U`+MHTf=i>FtI|GnZ&JoEM>cXryKh-^-$ByvsldSgOzN3p2%_Tsf0 zRbu0wdha&{bNSYiZ27hgi**eLbKW7JOi;3`=R}=jfEa$#^D!+#aprj+^jawV?kmE8 zyOIpmA34_VOP4F@=X%>ed-?)V5!M!EW3XbNusSE=L9j{GLj!De@TT_a_`!b&5JIKjVG;CX~%a`a* z1ukc<<&xp`Mh9v7B?^;ycN+{Ba5V*J8DIR2HoJp)JqQWtK!ySGU%vV`{q1wt>4gYciBb1MHW-7Vs{qaQoF1YG8(&LuWX<`tW(|X#^S$o6W+5e(# zGY^RVeq^jGJntngZSjlLj-PC}UEl5(#;wI#!n>9CgKVNTx<0?j@+R%vgXQqsAaL&o zDc*n6ZjJ@I-%6~V@4!m?*a9c0)A%gDSbZ-hpQ_1xk@d~}m1!#XDC0GxcYu&r!i$*3 z!#>|ZID^#UA;90U&U*?A7QxWNdCswp7Yw1yvtLSvNQ^myfb}-g(Vi@=>9iJMMA7GZ zb3UWMuOcaPg8w|_xBW=&=h{H}+ubm$xYG#)f_ni`m&B}M=kZ+o-oA9>ji7xonCQOe z`gYzy9u`G1g-F*bue69$Ra@|vckpEfrFd*!06{9``fK-#G_uI!y;G8wiiyJru%Teb zm{sEw8cw9zMbt@Ua&K@Spz5ft-GD!|3+s0ge+N;$C+==zpbu;qFksXB8t(O~@)X$V zHvLG{dq!?GxEm$gu#g)QFq zQ=}mFhn=rfqK`08mbgF>6$_jjNe^#6%Bb-;LZKRT%b?H2&uhz)`C7nVx!Acu?x)Q~ z`BAFZnthcBmIn^9MqPgx_Yr;4u2)o2!~axOv1boC^vx@tcOlkh)E84Zka$??h9Qcj z2z{hxJ@ombR)?ICVbX9o_RP@kM-g*KCYPzva#Zg)uDXBqKF360V;Yj`J4JguZBsa4a2o z+fQqGJQN8WnfGiF6&Ul|aY{KOYZB}xr7&u9A?@q5`-pmeAp1(XmQRA0MrJrSLnxMf zl;So-c6)g`P8c^siu}dJzsbq&*#d{mlZ5_K9p>iCQq(%haToJAjwUxc$n*dHf|U3J zVPo5S=dZHeZSOm8o8j+Y@zgcj**v{@Ws&6$4+gQRhC|$4WnOCcDI*VyFZ?>Y3NFD>nQsd0hp!o<;!XI^ zS&WOY9gF_)?qtFJzSm(w)lzLLusJ9*^P zjxO+#kU9GMQP6yI?nVfYE_)&1DmrU7D<#MR+UsVm-GX7k4H^b;Ow9|)%tA&1$#$}? z5#Q?7O&_1ke@ja|HnF24rAH^n_6(qze`xg(JL!zf$nO@TEM%60Ip#tm2d=|b))Vzm z0A#;-ejJOtEFDY2_cR4(K$?!evWY|yUU7D>c%_Zoh5C5L7qR^c@_l;5vTLZT_kwZ=$de$Acdd}7 z96E%5-Hx6>^?Mm(fBJM3>Sc{nu0!qSt0;9Tmj@$80rvT~0{P~EDHzj$(=Fu;pYAvN z({G-#$e2ri%!xD66X+FT){YN!dB(xNfK*Qsa{S>o{PUXg_F` zbXiG$AC##wq18&e8$)Xe0Wl{Eh^3`sFeV49R8}!Uf)4;&fV@a}J>)0eg7WM57h-LK z^ts~Q_~yRBX2`QKUuzluu-&rR=DbB{e6s{vPv%crv<^cjxj01|r0C&z{G-PK?t)t< zP5}WhI&r~{SszL0(33NA3+9Y;uPa7h;4?dJKsV?$NAqrOE^U)bNRqgIrP?xm zY;+=EIwoVJl!)$1Y!}z>o}J$)5DeotYY?go<~Av!_qx)5+M00X(^HGSdCdR4wF%I9 zyvn#5R%8%aC!w>(Z@m->+xD9cYH!tVyq!cdwe%6)dQYGm=UL3AjbKIOkl{fx0#>XL^ucay3m{0y*&4w5kOc}aen|qAZ|tB6 zH#I;rDQ?7J9n{WB|1D%-Au9N;7u~FyGX%2HcY5iczO&!_x z?ghkFm$LMaLaw(Pcc&0@S!(-x=}>p#o?4$4%?pNs`+(_HFDJhRuKeZOv{I_|^`Gdl ztEndWt{q!6-N(Xe^oGv`u@?;FoJy9+AMYGeq`)l#Ux$>JQ-peQ{l_d&R!WI|+G(qQl7I5*Q9o??>@H;)%o|Fcx~LM~&D?ID7ZS01 z_i+>xe0@vDy&)90cVRp-s*cE4N0&ll=84L96Im$!m4H#^QY3(~nhkw$1b4&PzQSxI zc5a+6sV}-+GQw;D0QZuZe(>B_H-_zzQFud@T*wLL0t+(s89cJC48p7L4l)l5j55S{ z_pkj0mUvnSB4zS@CA+qsbXXtRXFtt;9sQu;pms-la!+2hUi!gTJA60}E;dq zucp7&EGZROD@AonY1rHtSYfyaoERqQ1K+7{Dsq(O+j!^Fb9zuQC2xSc=^BM?lW!&G zTk)|iYM5gKPC{`HMV>aVBcb;x=ekCn#3&XT!W>30rTH#?UY$^T@9wB*%o(n8p8WG1 zYK0YD-{B8Dx+C##kcGkX>+Q4FZH<6mOV_*vR7d4vM%q!;0ucgZ@uxQt<*IZe6wD-* zYE%=|#F|RdTx8xT^%?g%nKc!v`MvP#YPQ3y3B&1Kf5qJYp`)f`R9u!y_?oZt0dLqW z&Y7gsE71)7fq-WF40R?R1WdzIZl2acVHd=|c%candE_^9tZ*I-`I>sA&AhMUr>Yj-j2;=)N|stzvwq2 z3l0JQSSokXN%-EJA1&ANqF7FH$g3QZ{FhQ(r*H87=a$!PI~T>@3<4nS)9XIa;`Rxz zB{D&&zd9oA1;YH7k!5FQs6palAfX%d1!rX6D#bC~^gVLtC&y*}h|6c8bI*+gK$qds< zF&K64kzjGT>o5|HP2FI!6C*hPtwJm#<7jho;fc$*`4n^6cy*a~E&U?-_y)rH@QtFf zcdT(?b!KxWV#w(RJvmm+o$SIx47fY8kLeqc>fhi+{aw%)>BCdl%$EPE__YE}z$&!j zgcYODdw41mfHol-T?MYYf&Y5S^D06g*;2QZa^7QYE!^8D7Q;XVq45=>u7y<5Q@;P63yKJg}Hbd zl|PilQ!i&L1Jgvz|Iq?~8Aa9NL;TQhJzG}uWblC8WIWyHe?7*%))=F$dw-(g3bQQk zZ$go7GH0_b+9zz`MiF4Q_a)h_rxd|el~Am(UsNOYaXL`~DJ%C{v*)+@!TZJcm^#Ti{Ka$36-wu#h{!Y4hI2cKq*O6jvP)85cU1yWPIM(O>{UQK-{$$J6mIraQ9L=Zq9I&gZt{F3fjGB zQ(dEa>P027HX3Xm)wcy4`Il8jdze)37}gtqi{Mkv<9b@&T8%3y`RcqL&Q~Q9pLr}x zPb7bB`i4$|RUzd%?kIE(mO>V&A>4fa57Zv>02h^keSX(p0=>!tnf+T_um7H>ju?mG z1&XvS~%++w}HbwPcW(}`avdqZ~gJ%xaNj_s5LB+)pJ zzdgms#Cg4+7wnN4rHyS-A+HLO4sUOD1$WO8!3~f9gV0B53QIh#x``pKK#c!K`9+O^p z+?w9%6+~l&X!{7ipe87`|(4G`6@Nhd~AR_{yawjGjG*qb2|IXvk`y)UoX=$(~rhn{} zsQ*|aT(PsM@gTO%`2m&Ra|d!Q?e}%DpwOxEp^;@89HsB7CZ6G|9Hg1bNvcu~o>mm5 zvEGCq`zdMk^dEkR-))NTt$&=KWjCumc27SB{JL_moB|_soUywfD^rPRsM5}&sVF=c zj(ykwJo(Q+BHtU&zo$a)ME(i1C`nz2`R(RV+FK+lD7{8QPiDi>(5CzE--Ut_kKOjY z{a^V*ZWR*i(VqMsP-}`&xb7u{pO4~qw{FXCu0H;oxwjtY<~I(VQJm`S{$p`He0G{2p2K#~CI7WOypb;tTQ zXALbZKG@m$P54jhDhXfn`2#A1Ucv1e=>$Z}#gv9Z%ar?+bhqv*UCj^JL z8Uk>k8-o|<79!wo#M6!SKI(k;F7!F(rzLs^TGo^y!{pgDsD+TJ#4zuUe$au{XUY({|f=Pz$DXZ^)NXfnjnHvZB_Qi;w{$~%P~kbDf2Ijq4! z5w0)b($^0+gG=z_K#t!qL>%GQA2sRtAH>bTkK_}G`HKVdt0I9$$yUPwVQ)AH>{Ts> z?LQF5|A6*-Br!grI>91{=}oi$hV*a(2jc^%MeK<*qZs^l+Qa_`2J8V$K!G!yOhTP) zQ2lSbuPNXY*=za&;8MZZo!o8|3(s7-~pfgh9_}G|NqVYUvt7(yM+tq zI=|AN*HxRi^?w*0E@)?}G=9<{mg{@Ri?J`)cc;3{Pf*v67d&&l$j%GDY4eNqy&1lv zUHtz*y*Pndq5)7{THsmPhW}@-fPoBJ|3Rv)sK<~0GxY*cGbsN>MU(~Yxc?h1HpBm% zZU6Vo8qDU$u<~(dvGN*)0X1PW%b_Ph7TymyHNQn{A5hqNVFPCir5I&I{j?LjahqUY zwovaM@d~&yNa0nzZ{i&87^fL(CLIpe(o)w&!^TDTKM=&L-4L4*OkG=P%uDj+A}aO`Kc&5ou+- zIQ#I4sQjZ)OfD83FGN%LX@@s|W3kMT|6b^bW_0akwhGk4!)h&RUgxe1l!5IA)nqu}J)r5QV&sG_uog#i82#_&^?ZKp|bh`CB!&n<>T_ zz86h%DHj z3kVOq*rMC%%b}Q=_>n)6zP5eZr8NV)4m+p$O$*(o%bhc6`-D+IiVQkUr)*bEVe+QM4#yTZUMq;Pp(RuXxD@lFxI44XUIMZ zM(7v&o!dd)h33sLMceH-RZ8cdj_iM|SjkyNyLtu@1~FdDmI`Z?%~!E7OHG5l5Zcz3 z5}zr4)bLU!#D}URfu|@%=IRy0LPsyz|7}p4DaQPn?7;oLkxwsODQ-Z5*dT{gim#F^ zf=d#nPxg61K!f133iI*jmoG3oI-rEQDGt>Lr;t_(-qCD6u))MiMAoHWn3_HAZ?8?6 zCxf9T-;N0~zom?MRh~3QO@Gf!!C;t2O$HF6GI2rK7(1&&vOs~MFATGDq|B2qYCZ|2 z#JVpOeR_}43FvHCJ8%b%iYC67&w%jjMhsijY9b{>fX za6g=@I&5>D7ie?7^cvu?5gD`K5-IX82K6^%*|Dj`e^_J|v_GQd`MiEy+wIqjHGBaY zqw356At|>*r?0$FI@K@xT2tNk>MrB=>2;Qq#iF2>r!R+aoeZ26oGMb9Wl3X=TvQ!+ zphWlWdIdU(hO$_2**Rbw%j_50Bea6U@Q;iZH;?N!y^5sR0Ys~03R1N0%aFnSrQ%@buWLyejaNFIf5r7HJ~Xg zP_fF0mu!cJs~?kDG^3U?ee3%I+a5>DB2{A-%aSn!#g$2iyq1aBQ_ z|1#-^LqW&lH?b2bcm%NJ?;qgg{z#*kM76OPw*jqyJ)kHG7O8qX2>&R$0%IWuj072>)Fz4HorxZ|AycoVu+uyKpD3Ya3A2+5!nD8PQs}k=@6|x5rY~_ zuF{}xhj||X#m9oFWlwyT6%iR4M$V`IS%X(u%V7-x*`GzK9~E&g9@h7ZagD^4|3t6l z^-94CW#{+>tumY;3k2r^1CxOJ8d2V}5)@{a&Iq!fOazV7&CK{br48r5e$H7TTV zUNeS`L1?r%412w2sul^i)r6l%gUp=$wiaubj9^H=mssVr&7GQj7t(fK0+nN;A7MVr ziS$MdDKOkvGFi<)93+huu)r}3$gP9!X|U`54Auj2g-HObqm@cBViopQ0YXK2n|T^c z`FUY}pW6sLq!?mdE21den24cvhBNBg1B*6Rhl%k8V+@?-0~t_*Ox_aYFN?WGh&3;R zk7TC7%)J-3HdIEc;_Ks-C(u3sYjGY z2#kWH)Jhp^lwn6YVY%qBfedt`q7*IFR8QL!X#2_mPRoEczBE&bL*!++-NBJOog(*p?VXbHIpgrsh4fqsTl9>XCaEOv0? z{12$jXf5hI^>{+^X88hP5B|X-^viH4(wK{g!(iz0%P*vBi8W@@Sp7!8x^Bl3%-mwQ z0s9gaAiu>O!xTE)`AkMETfajMLrSa7clO=CYH+Z{aXn)?TG{4=xbOL>8UBC~(R5=| zBbt(3I$f)4auYaMb~5gl=B^X-Dx&;^q+BOw#xT@~=9Hfe9s&DFvM@?SkM}z^%hTh_b3^C9**9KKQ<7=)bK& z36enn4r(owha~&wnit=wu^LfGg*bf(+90!XwR8?1>GV1(%qhV8*378<8CD#Uk!vzLgkOQxBUq>L&Wk z{5fh9sNfh7OurK1ev|RFo}15}Hmd9>q%2l%GwrkqJv~jyE$$Z!sNjF1gn3&oH{|%qZwfe1fPXLKk2}b0ek^G@QHcpaN&snIN7XLEIP}JmDq~h=Ct*ZT_sQp9}stQ4Rkgy6#VA_*y1=P`q zA|;GQ0asS=GS(LPWI!}}_W`nrRk6NEG~7@zRHF>QC{Qn31xv_p=1i~RRw{bs*$z{X zdj)YfgQ|FcI<3@M3RCFI0Ygkr4jD>98(6ia&fJDKQF-?1^?76x%Y4D{{B}ofp%+zA zYMwNCtVLmlYvVP7NkVvoP7w{XTdZDzZFoOp{N*RGkTvzjVeDZt6R5xT$a$sv(Gh#_?X;CEG0MLy?H zUIPc8&h1JVW2Mk|PkZDl)q;-MIr|PjbGDr$Qj+-Mrn+4urrE2rs zdA7n?biyn@0rY;OGfRndEoe1WI3ocjuThZV%JL}2HR-VkmS|q{z;C%1(wGP!)2P$% zLR_>tQqh_)X)TpXJ$zO`81ORZ

v*qGQB*ZV@T6fdH-UAHD!+Y)Z)&1t!>Qm}1>A!8~ z3`qIjh|Hf@VI>raSKuPo zF~dxP??;sp5}jWJ6f?$cy@(h!=6nv0!4^4*;=qfm&Z*Bj>IRKa2wpHuoG?+KDI2>(;au}8@NzO+v; zs%8KnhQ0)gM>aDkBPB z=VlEmat}5F2sCm4(P3M#F+6Jb>_bH5e{N}XLPuht;o3nZIWYP0FKY-5BQA)RI}Jp? zxlFwXvzT29DxERDnsCE$4w3RoxujWe&7?V2gNa!7CU86yM%EoF8%j-Tkek?yuX}IK zdy7s{tL3(-7Y_n*^^%&IV0mt)hUw+M`!P)dCWyeJGA7#uz7zOYG4$`!4~}RjOH9F# zWwK~~Q0Z|y!wM9uvmvW&bEw|WlHYz_HI9n<%%4j~I>uY}>C1+x$T~^Z5^`@-Ey%tL zx2d{S%PnX)Ecy_|anF`I+#G)$%^E`-tZIx*JkQb5gddBf+^*PHx=HaN6_9)KXfHa7 zd&01=6N|rh42*aM6R%KyDPqM+72**r73k-9;_puE=jt5j$Mp0G2W;<2$0gV3M_-Ym ztr!I}f+wpuVuosxrs~1KdSR3y?Y4%CTA(kH0MXw6V&0nsa+)JV7f{3TwH$0Cqp>sg z?nZvBucv~K1XAdUlsh357B5Cj$hq%In#~mM_Iv5pEs?Wu!Q$J+7Y8qH!X`XC72oH2 zE!?jRj({Olh&l{IuMw4D!Avjjp5QVDp$8e9%&Z1=w-)Z%P^kMbKzKS5=?UG@bjV~W z%^HZ_zo^!XG=zRHd}-Fmay$(~;BluCc5OjSXL>B!#t2y9sMP$yWW-N}wMx-S*b&%- z*>4`R3sLXu z(ng5T^g`|C?VH98{1YGWwweEtG6fGu8N-Pz!o3JR7l~u1*VK2%83S2Vr<_nDsne=n zWI9k2Vk9r6UTSpH>3xu-e6j=DCR(Ug0s{*WPn(3CDNHb^&kup8HX7 zjM50?EJ%4<<~6I+h@vhroT}wMVXQTowIWNQ#el}=Qq6qQ+r(jAX~L<51ar7zwYMQi zikWa*7WN<;dvWB7zo{UC*Me{xmy=@$W+q}}i9KIOlZnd(F|UzG)C@@g<23(9dY6V z%D>9bg5Wu;i1R+;PoJ_NtVVVXmvo0~Bdo?&OmxEJTs{n52^KLceM{6M{v8Gt5mVS| z(XAidkOJBy+89}6fW}aa(7{Y`-MDDKecOtp^f@~a?U?uZi~)s+y-k;F>r&wWF>Tsb zg1U`&XEC=Pygn;9JOo&|{og(kPo_%t;%W(N)TNjfM($fH4w=X0{k~mL3Zbp&Y6E9; zQY&(aGM*ctfh;k=5c7UyYif~dK*xOqG>wLs$L{5Kq3uyAVQ~*x&6fO`kd%b)Ot3`v z-T-X3t#rGd@pzRTgL9;fj2bStJl3K(i<`W9CiA9M#ny{l);jnZVb=xoqG~RrOzL7K zqx#ih+JjG>PQ>El(f|;?pQN9*77n0*6IF+AK<%9|(&0onYb&Mq6h{6YU zru%uV)wsx`4vG3g?)=A9!FLbH$xmGFcsfUHy#kKak!K_#PWa1Yd0-qKFRu-M`8D{R zW_%@9Ig|b%$zHwNaHZy@o(?ZWb1Yf`&umv7oL7ei55b`P*8s0_llz1m=g$d2)( z%}^>baU|}ho^YKtz-@fV8#0#?PaFq5uta-?ht(T?_AfCx%LfWzWN58Md}Okjq}q}I z9?6Dsfv#YbhP%U8UF&ypm(?4eN7GNFjsiKG&Nus;MZRA_;pyf&6ND5{t9?8V3(f*% zSLYSvmNHl^1l9Zf1!&PycM-PmOO@b4evsPOjT9DmU2u*-w0rRpy1Au4Gbgy-e6y1C z`b1cg<=-?Z1#oj+>FY`wE)kGTX36F}FKg=oUVHr&_zlngHzU!2lc_W>g}-mD-h@0sz~BZ%awH*6COnMDT^(Xg!HtNxTZhqXr>7 z1Ie`M@AWKHBxINHUN;a3;EXUWg~=S0#4IwTVbDRNIN<@RG@eS2D(z}#B7H+V5O3%k zLy_<$7rWQ_Tl*X2`C4EaO$DT$GtJI2(odYBRPTBrh48geRf;*8*)|Q}p-YKFZA7!i z1XcpiWTi6tvx3&9H?iVhmZc$N`fdyq$ok~SPp;VKuHmZDHnpLB&wK|GPO5#$jPm;kR;T!Aj#K-y3CtZ#ABkt;Q#6nd5t z*5{bb8I6ggkW7g->*Z*c_i{nAD?e1^JHTjhR;z_VCmjpfq9|_hBheW+Y<;_Ydsw=go=vP|-w~ z@mZeNCMJKgZg}G?Xz4=bHA;eY#KqcG<4=H!YO##YrR0thC5MwSe+`Set1I9^X zl{BWz$Ip2l#3BDn(F#6OwBx59X`G@vs?Z`P3dS7%CUDYne=DOb1zHcBLy8l`KfGttIb-eb1&bC1^6MqoYh^vO(q2ooI=m$t7OXz4%OY{GvQp?8TTym% zQb<@-@jN;)zW8h24!IUdgF{AxGCDoStazq?sc2B2*6oIXMzFvkFymx}YnF?a1yYuH zYWSABF`wMtSeC4_ha^2dkbxG?QrQG$iw80Zd2}c|#Ei6ZHn6u43m7>Hll(=d+H>4j z85P#6k5oZ+f+vaJ!Zx4F8Xv=Ae9liC68JUs#Ok7NcKbnu)e1vKS;k4+W~!fQ&gAnd z)cArcw9Dlrc|}f7IBzG;29tvioWNC(!2vKXM<{HKgb~=Jl4y;6VB<2f*nC+fD7W@( z#-0?$*v~{U+c$OVmJFIM`qCtiVtj0>8i`J`FzzU;UJ{Bey#|-k5}V(=OEKe7DaoM0 zs1}#BX@`kg95LLnh>vnCQ7pu2BQqc|`=^kutHS7CAO;ivnK-XCWN#!x$Ox7sa#Mo%Kj~83Wn%)X+6V289Gzz z(K9l6{S3-Aw>_3NwOOcMBZRem0VdJlwCo7%O;1XV>Ihj7jUM4;TT-k2M0aorbuUTi za_z@wjw;y~7GhQSJfxzq#6}icaI71ef)i}BCMco9TxGjHOD0*rhKH|&iyqco#BSru zf*9HAK#Rcz-EweRMJk_(fg8KWmz;pXcyV-~G^k{OizXL3T*pnMQ1Yd-K(dRveUWDr z0)-)fjH>3e_-{xoB@LY~`^7fm`4V%BDa2P0qGnLh+@pQl%6H7jkd+3fJuZRq4o38G zU@&8~lV=57B9?7YrcEMfNsf$=h$gOR4Y78z)(gNZRX41+eY;cND=-rMeGm51K`1#rB&y9~iq&?7XN~NA18lRk}4(`&E zT7^wZq+KxYg@e?!NpX#n_oe13V<%UG_@~gs7;f4RA{%J7h~l3ca5M%I6gdj+HOc81 z^#OBjhg8OQ*y=m)!y3DJl3xvB>1mKfRckzIX=(z;S}El?qj^8Y@Vu&sLEj}vC?<&| z*igVez#(f6PB~3pzu?|eq17C!#v=-6gOHoQ;WISnlFe4>4#Z=U+FTsx$(AhYJVu(f z9p5ar{ic_#=hyPV7*$ZpF(HeqEq*j<_RaCdx0Fp94%W-IURQD|!7asx*0srJV7&2< z>8T+hq5i%q{?%F*Vp)0dw~ehisqjjTmIfC|K4Z9v^9veYS3_q>CLSgpqu0lx@)8uC zWzW=7bDubWPb1bmqE??sEN0=)G^^Y*QYbwWI5dl-(G*(EAG^ky_}HASJBvSj0^Fy5 z_Xk#UFg*tDw{+;y?nTuHA%Szb=J(#1q4KW^KtV#G3#HDiZ;IqC4$}}~ z{xCCTC1Xsg6wQ$aV&(ut?sT#DT2iYkn@MPY4q+;yL4RGm(HK^_ntjih6DbCis4mz;S<( z_#EWE5ERi}(K`3SM^XAQS<3`ujSvXyoMK(d@T7Y)h`i z>c+lxBXMQ5B)4UqtbU3ITwLx2L3?M?Q6#+c8b3M7OrpEEsP1#i3r3sK@e=UEFD%Zj zDXI5)qRHX~)~Mi7w$d2SVV@1jF%^b2zBz)pnfw>X>lo(;r0m$BhMEdAfX}9YR3Q% z`{`FrKWytD-l z4w8a|wy%4ykbskP*s9o^*>Lk6L%V_0R_OGXg?Z5}3(X5kpYv6}S%bahg?M?`Src{q z!%Tkee`TW+8Wyc8JR#}PlcYqRxXv$$h`ujXQ{7=ndhW^! zMa7dr7Wir&xcSA`^81nOskxFjFAZWA>`KD-n8d@a+zm!9Qnxtb$zqx83vq5Ro~u$p zhl2`ehh)+9f|G;yVJ`l)n}iEoK>NZk;}b9h*9YTJ(LIn=l~C*DnbbLuscGk8EQz+7 zYiJLeJMxN%u*j`SYugDAiBxeHOAMf)N42t*!h6+`QzBTm<1@#rxqv|_EdT@gqHUX| z?KFj?R5QeKb~u$2D$A>q zB=;bZ_h`g07KSDfEJ@ysVwt__nRbfhAT{*A%_n#5F+ z4rt>RIIw==)xlqp zTCa{_kPoI~rv63HW?EVzq7aqRT%llA+eGLplo8{K1<#bycobBT2ciWP^R(_!BD>V7e{HY@TQ#*B_esu?}Ph z@EqsLlccmHf-r71)ObG#Ay*sw_WWGM^ix+o8X5VW=p|QwFpfK$7OI5?J1Ea$Z6Ru2 z@&n-ON4f_dgL6_R?kOg)Dp#G*c45fh%-=ugtcejvIn06<0(0h&*w=S}!7SExg@=^S z6RSXr7Fyj(w&*x(M^tvLHSE|CdGVHVI-=+n$x3&!Gw=`F(VV$6o-yfqv#_C#)gV}H zq5KmN)5>H#h}-7dTuNiqq@}Q2Sc{@Op*(iGR)v{# zfCIzD1Mon}c}W_|Bejwh_do#|wFD7KL}~knJ*O4AKxIJO=t1CA%H5$OArT_-q!j}* zE(eEvP5Q8$En!pMhx@B@QDWzu+Xl)ZYIBu(sqr`JTnk6nFRA9ISg@0)_ovbOBaYP4{dh z>aE|x?w))dwD~<`Aqrqx-a2G^mzd(DEPNdMs>#$M4mhkOt~Z>Q^WuynsQ$8e>QrAw4?O9TFr>f&?B!bswg|1C*srxi z(IVX$hpB(xW*L4(PJxo9nNy}o5|u1FQi8(O{NBz5&x#?=(c9fY8J;|_4(H%4LaeQG zkAqk?L*Wtx`YFb8u%o8cCcp$28{52zRtj<=!j0>^mnVZD8?Q}3IoQsQA?q}oFZ3*z zbt;kSZu;WRbS`GhmfM(!8p{yn%0)#?FP13FP1j6UvSzyJ(m1(V73388VBkVa2aPXS zE-H&@DPGyl9d(CY-Rk>g7_azQ*R!&OCqmenag`cZIygyA)b3M5d?3zr|8oyzLM>>r zYKs09!%UeobW@$Kw7d)>X28(czhkR&I_f%M#!-)PzeA= zB`kX2;q6Iuh#(?}NNXnf^V#X7DaJ9yk&L3I{zQ|7MlVoU4Up$Koj-|Z!~7eKnqdt& zqL7b}MF^qU@+3~ShcI8^22az|@F}VC*D7G6{k|c4 zb0B6>-@nFw=xi?v5}cPk?N?;dEM}XtJgMg?FPwP{3OUOiz{D;(>yM0i0|-+h#X-Ji z)>$INf0_VXw3yn89J$vqyD+ml&-x$na4_ymZerJ-atZUJozrJhlpPUpp0^}#6IK0Q zK}s_9+*0#v)Y)jlbFUa0{3-pv$%h$Y|7cxe*Vx|kz{XlsI%8bRdxF@0Z0}#~{{PJ* z?eP)$WGDUsqD3bn0aIG7?>)}lSdmzsBLCBfLHDOp&6W>wVW=E2u7XO8F>hZCyBB)L zUxot(;N|}@@>k-Y5kAi70AO{2InVyGd&!B!jQ$n)fAd{gexZEQ9sOd5_lkzfX3@|1 zTW582#m2k-ixR1c^O^GgU>ULu{7TF2Wl~B#Rc0z}9~eiPJo%s8*!qNjbV<_qE&lRv zWU+Z1-L$#NvTk1gu%7>t)iH4U*TU`QJ)*^|WtKdZ2-b_Zq0ZSI_rF>Mdj6x^-Wi}^ zvB56g%}USgKhs5fX0+S?*Gt^}qx2q~_Anqd613WR9=kar&^`tImls6-)y0DF>Ov)J zHbL=!S-Jlg7ntFnhZrEp|7Bys8gd}>F6VPlg9G?cSfxb(Ea4=iS<4t)FeY`W|2>x1ai6{4YS^Wo z2MGs0ufyN;G@tLn{jb-j1Jq}k(p5PAznG&~U%-KpbK)*%E*+6XcWnRdtN(D>r$Yi{ zN}Y;>IK1o3O|(SHz=zmV^QeRkphgQfv{ohchvyc;Zdu}HtL zmP*^7c;hbo2*3a43!2AdoEbJFp$8`L8V>FJalh8~g@l{$WAU|IMz|bwzZpa1NGlJ2 zt(cDJt<#Hv4kJ+#2@ifqH+sr$iJ+5{%;=x3PZnE?d8dcDdAWMyULp*AXlK=nihuGp z)jq+2riX=4_#A!~6+lKoC4bcB_pVTZ@aY|=aT=q>IaEw3^o<&k(<^r+LX`l72Ly+_ zz;TcBSgVRifAQxX;-Nsh`#5M4ahANyqW+Ee>R0#uSl{wltWE-`**xQGzFVFaGb)cQ zx!A>v1@Z^gy*I8CUz$_lh0hF(&Icilxl!&LjpxIctgm;8Wlo;)WxTD$!6#?<5(ZKv z7;Nvc_lu;<$NSrKAHQa)n*E;9@_fI8zr>Up-^p^iK-qu1a}Y4_gg2R=Znu9;***c^ zn@=oJ@4NlG>daC%A@DZM zicv+6&a{4j&3>1MgYj7iM;XtN*IwvO(RdWL{Q}q2_?eF~| zU5ITPvHtUhuV!?U$Jw&CL%(pc?p$$G8J@g$eoh~AhDisLs=jsD(m`|}&TQj{`}1Tt zAxmVIc?6R4s<%h0YM?9fMm!^}5$Yu+&n?feHS_9Q7o(+>!zf-GPErAbEYUoRZ+M{f2YicpPn;*(xZ3tz6u-9x)aLd2W`AP-W|q-+MaI{X zbuQPUdlRTNZCS^kHN!7?D)Uq-TvpZ1QY#JUoo<%{aq{#D{JLR6ZwHnHm#o_P`jui& zKmEeX!V0?<$T08aEO_f-)W(T=mw(fW+BpH8IQ`J{w-|#WSVF)&(b8tB z1Zl}u1Y4V4mS^UJUZ!@nY?JYgA&&^bhd@6+6cC++o+vjoQi2as2x|OKnpO;X1Ak0C z_Oe54KmS=cR-Pm&IRLMjkP=U^LgFp|Ev6zy-Rw*m`^!XGu{-kz?vpdg=!q;dyI~c+!eq zp~*${U}%%Z4BepNVVUa)Z22WpBFNj}?E*^ol3h5r2`gKJszXut?n{+oUO1IG{1R2X z3d#J^{{1Xl1^(O@YGeaNBPPK#`&xC*wea)u>4HXP!09=cp}c6qVW0+Y%^-=ZUT0S}xtMh~139gtZ*u4AHql)GfZGJ9Mg1;VL}PMPxVhTxqkU-D4=o zX5G*Xn=eJgfg2xKTV%E9RDv*_Rj-ND%K2XGKJaJMNUTR!BTDuwm|*fHO0FJie9?NV zu{e*+W(&L+=>A)0KvI<+qzR@m{tYU8InLtF&>!cD6BbgpMQEFmr~*%O`LZjkl$`J4 z!xlJ=OSU~(;u?v8?C0!HGR>!LEi|Sv!(jatLaqpuu@E>2+_Ao|Nr|1T>+03myawX_ z4-z?DYe>eTq@3TUMM9Fbdt$Dfs+y|sU%G)8+v~v#dzS-0Oiq6JAyI1Z6(B2$&4zJE z!8bguE|r`U8-MCy<%mce(};e`M$TucLLx8j%C)1?+l>aV?56S3JCq0_WG|TulhC`S z>`SRYx-mvdc&X!%Rl-WE#3rXY)#wq1g|T5f__9F$!RIPs85#?o6l0_j_!jk&h+v^kyfZ!Enm9If0P6(+Vz7vBN&&NPP1z#w7&av;NUSgbkT($a8cyNPi|3hGF%+)5nr(fnaj; zb5?ee;Y#1v#Ef4=AM0N^yTUQP zN{6GMiJpApr2_|t6I2!#_w`FN|J{`iLqk@?eo7O+<^3D>lvE&Lt7-K;w=-LwbM%Tz zgv|B`<`XNP%K0pd1V4+i7XVvcN2zwj05z?%&VVvFp~4?5j~nK|alg z;p|oAXr}?(JBKk2XfTgw4hyb@!f;Z+YkO5`(s;77WcuFy#WVHM!Ff5-c`fZ`!^^Pm znWpWoJx~%+>j>;`>mPo)8k>Tj%|zRO9kr5`zxlw)V%vzq5$u%64V?W}-{&s}($($m z^byz7NyB-Yv(h~=4kC9tbC}vV=>Gl#AR@_`MrK)K$Pj zRLc;2c!DjJWVN>9GN~X(XR+ICJ7)@Fri`%rxt)XlI+BEjn976bAJLH_I|1J9=j+ho1Snsh$V=1uBwabB)9SwHT@gVjPQBtYV-oO z(JAh|Qg#mJBsAUCU|}RVk~HQX#uu}eDsGVtdw*CblKcp6GQ~w=e1~1X%^p^)t|RQX zr;jTd8kJTY*^jy-VPPxJFc5qO$77z5VAspo3ErA-G#A|_-^g|FW3O;C4tW1D?XkFT2$#_2$5v26c3aGd6dDV@Ub zcPr!b{*T&}X}s3O_h|6vM@t)h8ZxDnyVDT64vug~$sfw!=Yvl{)q>zIRvxH< zy>orkUmz)yO;dKe(Yh&>++y)#2H}Vd?AhmvH-J;skb_QtY+m~z3r!yaN;B1=_a zg-~vZ0Ph@INB$Si2Z&2)?zK|V$OuVSRMhZNIF0XBm!f2r&y-^HuJZTSlxx@-*d7pY zi!%MU40=>m9nu3hTg19W?i>|(viVamdO53?+RogI?%9%q$#0>EDlo;J(ZZ>c!23Y& zLF^T$Rsw-sWaA&~pVyTOA?c}t7U$8!na44%ilH*9lI_R)(!^%RE`wZWEtI+lHFmo$saY8S5-VBmB&&D$pKc`(?!cgYZ! z-cab}#o!QAjx>@JnBv80GRKllyK&B?!7nj=qpWChjjl%~^6C#AoEV1w2!z!3p~1Kv zka;}PXIxCe_J-L0Ies4BAok;pt`x*|n?PwRMj@#d{niDFv@YChu?U4rOGMs_e%o%6DFg}yZ$30+d@MLjW-M&C!~MP#h>nT?qgiv07%ExyaErlCRZcA5;Z~5g zJg}^gVw@aUGW9ORc|)LjInhGr;Lz#gGb2&eP$Ur{2sZ4GfTp$06dy(*2Bhuxyldb4 zx?x@I50wbkRXm$;DX1qsj4M$0-tOY8VF$x88)>NHgdv^7Ch|jAKc$g-ZNcLlgAQll znSS{NUQ&l8jn_ayB9>P*i>>I*!`~gniD2W=dvxh4y`2&f5WR4VcG?VN9`GVWekJ26 zj-skwBq5azJL$2~m9CZSfzai@T{H{vw1N>A0-|@>L@2hi)pd17GkC~o60gKoZxZ+N zA-@Fe*ZfjJ+}gB`v+H)79Q(nq?1|KOvD($U+U+r`p!qVWkb}dI4RNZgmJ_g$QGU}W zWpCw8#fjHf^MV^gutGd|950N^3Ly8&5KC>vjYfQMju0BHr)Hu+rH8iK~kjwy4#Z5RjK@%yq)ar_Y3cShnlcgMvUK82)_j^sFl{5lM{5ViIfpUUzM{e*jkvog<7TmzTvG z927z+)2PjG!C62s?~OX@I8(xYXQ()=}WD!?egF$&ws=}?rkSz7pr zgQJT$RijC{(qTBW%bT6;$&(%atMJu?uG8kXUqiwALg`^ZpNM{F0~$;X>m#8>QrAnT zi=Sq}HzGOLD^n&3$c=t#W_wmG2t?(`23+yQl;t>WMpE!2K`S&TEp=kw@#d#wp#ReBTiD9w{Zg`|u4^L~I-Ry-w>%=^j7!{K|XX9^9!mzJK zj3BzMo}kt)-aZ+W1M*F<)w-H0oj0c%&AqU03C?zY7o4X7%NaA(j@OQGNGppdJLZfK z@HY0Kv3Y0SLugDM;AMsWRuqQ$kk)J8EEtVAOa~M!sl1%|#f}SmcrO@9NTMhw8V4i@ z!SEnJnlCS+h7I(&tkyTLm4u z8d_m>bz`9rzCnk^lRt4OpC0Px%gQ^3>x9PuN!B#fTSN=vUIIGIG^RP>!xXcQGd7QM zY;wGF0!?ujj*t+C?MvfTR+uKxk8s#*Ewor!#VA!ewsM+TyBSm=Y(pE0w2sJ)2x~hD z=5!2_029L5@f^NWVD{1TD={NA;w~;xlMxSqMjXt%`J+ZZ3K_a%x^84b#M@dw_7}3~ zI{Q%CMaj8wjWiG|#X+-#@(}Ub;(Zgo$_|Ig%fix3 z99`}mxS?}*+)7x%nQsT&9n2ENE}Wg)z36%3;JEF^+1#jw=RJuLj!UpgVX(I*K$;VB zD^O516Rd}P%?);1hxtc;KxKe~LxDbAsmc8{)lIjWxPas-L)t9YN_4S}bAl>=)xBv@C_ znvfY6s+HeDkmCN^%1OPgo}M!m`z&+qx_N+bKV_CrWco+04YNI`zoEC}%3czLAtlUTs`(sAo}kns0Y% z2wvt?KCvyD1`sg7R~;y%A>YKwZzQhEwMyA)qK0GIcS5T#N{VIt@RPhC{)|w?0Ttud z-ib#z!8KoS%HDoF4mION+%5(1G*-cwv|T&EMMVw<54$Y^-V;q^0p?)^MsoY6CQvI3 zxjf-ZMlFAc7)$sE4W9?!pJKaXyPRqV(G>(H_VzAm;h-X78+YQKx#?CBvUdZ0gHP|*^f}>r`?eXgBbS4y#I%-cMPwz z*}8>0=-9Sx+eXJuI!-#aZQHhO+qTV)ZRf0Kzu$Ylz0bA(t~J*z+;!Kg8e@(*v*N}x zw2#&}V>1@PzJ$dIinWvRt?aV9h%ZQ2st8`}3XDuyeXSz_uX3*E z@>NCJznRg`kp$hDORsW6^g6(aA=vzU!u?4D0yeN*0<~4%V)))B)f#)=@p@88Ykfh# z!}-H+Uz_7*B4t%`Cd6wLViFg>lzbA4y39peBRHOln!~4fd;C(lMk##djFW~Sy1;n)upq&Y2*zdBzVMo(=O^0h)UdyKKTtC7M#VQmU4 zAxhYBgp4l;9vF_n2o1-ec=LH;y-ql)%}^8|>zir%{Xv3iVc>(r&+KqIi@oefCADsr z^1E7#=gnyi)Y$ERE=*vuqk0VI!OmAUFc2M zEwolMnl2MRuaYi`iKJ2dX@{w?U}#?=-S_XL7RnkOlEH82wMelWV}sbLlCgN5qDms) zt7%HR@pOgLaet@n{2p#dMXccGuGO%125b=r2=ZDkBcZ00a5enFJX3=}ir~;Q2A6x9 zgD{XmL69IJ#?KOtQyk#|LF>1o>2)AGpN%=op$+*M?C8IFU2z(pm`Kx0Yj43nYGa@e za^X8yF|{Q)|8hPJm*S~#)W&Q&rgOjfxFSIPj9r!>lX&TojupsPU(As8oy7nenigZd?mE5pR?F6m#Z(;W;wt8#xM@nV!ch-k0iAern0 zbzInYaw(l(J&UaGtvEO$y(dg();)^8r%k!dz6M38FH>zJBbeZGDDh?iGXF`wcZ$9< z=yBa0jgEH(Lf5FCaEPQVtw}uWW>(G-VSpQgQ5BOafJ_Rycd}5_qrG<|8CAm*)9hOs)y5o3Avh=c=YcY(Jd~b3|`rTj7KawLKp*aKX(k-Bcw5sY5Io0 zh*nAQ+gv2s_hMX~;K4z>&PZCot!~(_Rm{`($5?R?K*$ zZV{&9XJPm9fHhO^s1HM62B`}&wwAaVr$cneKthzOze#>g@4fH?)rAI1b!@vHh7pWg zScbBz1c%T4z&^5xTIm1rV%K0Ox`l8?E!J$KK0=e6{{w2hdB}&~2U)y|W^Bk1Y=iDA z6o(V>fS3zG%P4p00!euHNuU?lFCn}4khSNa$L1=ijWs5b3+WdbUpTneSo}Oux=4;m zwjs_Rp4b>&H1BYwmb8iTt#G}Ql2-zv`FN0s zz+y0g=j7Vbok1CX+vcr^iW&2hdN8I)yrngZh^%d_ZPo9ch(k&XBe@!?<4n4d(+)Vh( z+4W0_>@hZ<@E+(wEKT;K6A>)ptDHi`-rl0zGT@pzHXru?WLCT?HPYsEBgXplw2 z+~=MohQLPkZ6PW}=uS-1`5nS1*Oct}?CvLfn*I()F9ne2o`N`ib+QLKo@T=#*JaA2GdGu(L(nf}Js>RPfM z3u7Ikr%KJ3NpJeSzqKNjya!QZhE9Z>zhzj?9XDKFWSM}jW#N45qxZ>&&@SyCoAH?s zJ~!K|$nLOS_(~xxS@o;l`EJdp4aeATL*1Rw@@3o_QR0J5mdmxRtcFVFyp`ko3kr+0 z9=g-49ww-bX!OetAk=5?IB1nZbas-1u0tK_u{4w-B|?IDs_lrN2SBLiP_QuqH)2A5 zBs*WC08**Wi2)?<#twur6LR!De=4oqHU%d1(t4NRxIJ2Y?_YdI2 zV^N+(jI#30ZUp*{g}8A8I?tuw!N|NlgOhbMCZYqfLkVXgJF2v_a2Q!`!O>yIenP(wR2iBQn?TV_n*|ICf372 z^KpSWXlbo0h=iYwyQ@FC{b_>sWlz*wn#+2Dyq;f?(}e~jUn&2VgvDAdc0x7Q9g({p}}*0eyE ztK-ch>}%_~y(`82Ue>H4A1?`2C3c>T<>_e|*AaS)GRnx42^0gk1szyQf|^>MvgmQ` z;!@AB*uipJ=l8b9X03NynwRyc(*$T&Mi@iE5P}{A1Hc{zDENo)E*hbp#B9gA;e$2e zYNaON6JNsK|IeTW3yADw>kLotk@;?Vj7#nB&?@iiPAnN&VS6utu2sB@BbKmR^Sih2QEl;vE+YmvbRbM;H0G;T>bO^_U@nQ5-YAbPt1(4 zt1AXsB>qaCJq`1}`oj>|%S(8Iii&Qr{$>o z&J@f-&E}z}Qg8)3*(>IEbU8BU(n&`^=L0aMfYv@wiT7O6Q-Qfi1&sCy?4wiFiAq_|RYm zdnkm{czjVr(TVL~D~iy4&yOJ)oH2Aadx)q&Lq!wA*YJ-ax=U~$jeA_4pP71MMTw8b z6r)wX#*@lC+!gej+`HPUD%yj^utTBlmL$OI@klC5t+o2d)g#lr7`kzG7^}E|6_AVQ zm)_2$uBEYe5VLL5xttvUjZUvWm!BiArDeg@Q4YLz%SmB_vGc-%wS%*{x3GjbwIs7( z#C2f(`N(24fZTVrKTY7XGIuB$9-Cg_ZxYL`Nn9+nHg?Hj&5m&(Y{k`dN%k(I5|SSY zb2y5FfPCc?;3*-Wnhu`iLeQ>mzr&aO1qpfuD|nqFP#?wRTJ6Pf(8jm^yFNnw?F}uo zWO~5StIup!FX1^!{IppM<_{k`o;Fq>*U)3a%VS5Bx)%`!{gaDjKt%Bdj|L{w0u(|n z5p+Rr6}oKn{G{VyP!s75+nkB`zuq2zAh_?_TPNh)yW5uRQ<}6&P(z=vJZqo9VH;(T zqp_&Z3C%|;5oaSbsgu&xMe)a~c(GNcN)Y+&2o@%un}fZ@kd`y*yEEAjxk&Sj*`0Cy z{2hrhj?a5`b={-r-P%=b^aqq7j@uC~3A}f?dz+n|{R~uVXrL#!=AcgwE;5rD0=|V6Y_EYROY=#uT754%yUDTI90E4sfK8k$ z^mq!wam@Ck#_cO$8$|{2$@7sm9;_?s_+WInbX3T*oj17q5oLJ&UZMVg+cOn6;yUNb z1>0~$eF3mx@Zuu5T-3lUIWehow6MM0n={qfT$168SNiu)!U2Jh8$h-B+BtG|Z)S(i zLvgD1it~qY!xu5zp5YGm7&es#Mfu6$dcxITJPkai_WgUGJNNm46Qaom z!b!0rSMin*#ZJ`#?_$4rw{3LViTK+gs_Wgtz<*oj+>rImep>IF4?DcoLW!dKP{yQFrx=Y24Fr z*Ol3BR%OcjpB?QF1O+8q?RG!bGK&ZL-8n~t9kMeist|z6f38blFsCQ6tmWyyUZg$jVvi3+jX*uCNt(lfN->%q4{FcM!`q+)hk>^kJ}0qV;j!2}DlMWQ9Y#A*cP9FZM%>hf9*N4`bXMhkQS5MA7xFyyZGM zi#m?hl(2@>gUSQ5)E`$U4=Js7ahH_t)i=v?{r}L9^E&ne4j9X8Bp1r42bz9gicsc@ zY7=?Q9cD=aSw5fA=7w!5N2V3bT zraT7&`m2_mtgARj>9=yBvwAKxJ(Yccx&%9wP;I4vM-5}6v)`1%qF@$2NL9`Jq`48QkuFTW0;}2agZfo`zZeJRa z&Z@c1ji7DsY=jGC{6}B9C_Jx29JQ9b^Ta0mO(h-vISwe)=;(rL8S3UG`z2^Deui7J zzen*S5W=;R$;b)#=rNrnn=lXn3W%n2gEH7F7e&HzxdAIIxrCom5e~MtUTBsflf%>s z*$87yb#7~Pi;-CYEgqb?`#0AwPtRUAu4XKq>6F>Ss0Zq63N$;haNlP44AiS=tR<>@ z6H6V455u1~4(J9;uOt%rhWcZV*(|Wf^whgtk*=~E!K5B%3~y;Ga~s9LBR;Q)($DP& zQwn$#Yb{6>^#P3UP2{@;(<5=H(h8;XP%=1}GDLY-CtnpOTz7(9Va|a*l5n!$*9gI~ znZYZ?X@m|_K6SRYLWK`Fp?3E>zT!i69}in-7g3OX$}K#|UK+sx!*5Y!1X5H|3JNvA z{O-UyUK_~LI{Yv>>|asum5Zfo9SB27g`l=!4BG~vhBNsCFHXW_Lij#E9lBme`D?NG zQK=E~Xve<3g#S7F6$4F$Xb-CM%o{9Q^&Rp2rfD@x@;&B-q3>B|!5rW)CiD-14*?Xv z+Utr$5P3ceYHh{4IV$864x{&HdS+?H;RuQ_TOm}L$h8H8bS^xwDlh~`IS%LJ^NBgT zqPRbXi|$C+)YGNX4PX6p@C2--JFIBBH=JWrp(IV!2>T*tgJHhlW3e|ui3HYYZ8q_p z6rwyvy|$74?kn)xH$}$bwwyk<4R2R70kzOJ7v@lu`Zi;PgDIzB8c$B`KB3@0zh zVojD*SvSk93m=%^)$Tj3rSl5OTltdV&}9PFsh;BZ0W2TC)ORs{%{sw?p96H2*gPv) zBPy8q10I2WmwLcRgs4B2)n#D>qLsn0nvI;Am6K5Eecaitz^ZHAwJc2=Wy8Z)p%Yk}GQn*V~r-2z3w+ z+ja%-x=mGeY9YjO7pxCe%SV~aJLyA0vRUVB-cZx|*rIO{M&WRUovue1`$senPVewM z@!j#Yfx^t?{Rw4ZAVP@{#=hboJdqR}}K z`(bR10X&R|;A(I(L2p53SZd#Lut0yXw@i#7OX6EL0WB}D5tx}I9V@#nM@K1LA%CUj zZ>~eRXuZYy+UOE!duLlDj5=O}F8CKK58Yhw+7Vqmu|y{zekDn$u@Zr+Hbx9^qg?rO zT(E3K@SDT0&e*(VpIl@`o`3PO-G5)9@yN6pV-G%%*w~Or@$Z&=l4fAFSG%CH)38K_ zR1>RKh`t^^c~tDkt!jb~tcvKG`lBJ0XtJH8Uwyj0Ya)f@)BN{GnquTD!Wguz`G|LB zsN4u^85C3yaXTU;-X6*yr@%x*Om}gJSi~GCh1G%^0a`fS3X!oU))&SyAPhH5^NEi5 z%*+6sGG;JtY=w4Cwy`D;_Z?IY7%#l-s(L^c$jv+Vun}}qP>n6pOKp2j?8^yY z^zBSi>$8t;SB~v(u0O+O$ETlLLCPZ2K%uz8-{OOMpWr zrAnhtj`MGVJfT$*(@|6?2+(0Z(o40EK)*f+bvKk0rX>j_w}Y=(22BG;R-|gOe6}4} zeL_`%t~$21gba=x17(rf6>?N5*2xYr{X+?#E@K^v!oc6vvkXkq&fF+#xdmP}O_*kKm)E~Qb8pu*W%)@_Q%&URSC8BbwTls6ml zb24yupg`5pH=!24Ih#Ut%Rf{AG0njbm}VG+kX z-7Q%iS5!*$RS%ezswF&4liHuBM;w+`?fr&6ax{m87Uir?q4)vMM}_>XXX5#D(N07> zI{-JrMTmD#A@UJ1fILZ1tQxE?DlR6x82Bj!a7KUGfVjb(EAfG7{#bN2*>TTL^DxeQ~8WXX(No>D>m zx8#bpgs<5cX;7uSx05-e=rM4+t&tP7Ssib9&U!s|KM&jPsxxPJN4!=-pmKH(&bf1z zP?iG&ndXAEKw&GE1#)%M(bV!7UZ)Yz?@s{xp9qDO;CfUwX|g>s+__|woc{^JL}a^@CO(bdq<_i zMFv-6vy`tgRhAiS6Lt*n5{M+cx#%)m#88J$X6N|()VDy0k^2=Ktib#wiOX`;3JuY<*iJLUVAn%WJF6$+N80MaxkP`%U(g~T`9=eOg5BS znFgHosH6~FDT9*-U;{+CB)cvew)f#{{9p~Jw{r=`bairBvRZN6W*=M-xtTK510 z8UYL2f7}o}R&xf@6)G{4EH8~2q4pt$$ks;v;Y9SWqJktO4K1rf0T*b!rpXzT<~WYs zj1Mf6X_G|ZCGQIsT2m`jL$>Swc{XhgTNZX3J;n_S8m#H~@-pHKBMf}!4IJ4xsZkbl z!0Io#2-H@?lL-8L7g1hXbUhGhZy23#Qy;4|~x0jtA?7n*9|My)w6Ocj9Kk4(;bK*Y0;$3KGTANm~8kMM&J(ixk4yHVE}VJ^4n~ zZa#0cqC+;A943%Y?{|=#&Mr(e&-#;RjF^$&*7(P5%!T4({W1KU16t;FYArhdofi#^ zA`Mqglmxt~A^Hl5TjF!xGoTs;X2z|IMAoQ>9@k(Ht_XFt8p_V`a&6TLvC2|3*WgU(Ep17RNZ8`CDx81GcZ zA#}F!xDyv^Qa(=`e#_C0Jw6Jwy=8HE1zF$-R(+y7VWU`_9h@4$8WRp6m5fPZ{K1P4 zRQ202k5!|-5I~q5vI$&qMq@b;?F>?r5$d*wI0SVbhPRi{UYbIHR1(VRdvrnb^DmLD zNZNjy$mb#~vNzbvot!n4Vc?LkR5vF~Z=Y}8RIT(ro>)+uXJzGO7Rd3b95Ux+Oo$tb ztNudTrta+RJ8mB1KY#Y`=>h6p166Ah84p*eV0e6IVo?pue?V9~z#I2XC!(pzg;#uA zKupQb4*&j)-`iP|ldo~EfJlv4aw zMf{lBtF8kdCIu>ONOvEtJaCsT(%YZ^D7*3;m^+depPiley2Io=NMhi)Fg6BR*JZdq zK792r{1|Yn$KiFx>G|%?Uu$~G=HOU8h+@|X=*prytLh@v;QMDHbi{VE(H^e;Mw?ju zgCbkQ0>8fgY)rLN386Zko5#$?%dV|6tMk?jDXpCZKv=z9p#XgKYDmY#>VH7Re_^t_ z*#f<>xhoi$wsHi$SY!VI_Tv460YA%|G!*|Ez-((nI6L<$>_Wn|Ik+E{!Dc~jn3`?0 zS?&b~2j^z zWGds75ubCyzrf=Y;@GT&0MQ6gl(FGsJ(dIo19L1jIp*WVcY^&JganEX57-S}q#8D7}ZMB#M zKxn~e!+BO)IRrHn%^$j(Tp)m7RuFQu=W2IItcr+P0-7q=J+;mHlt3mWp=Y>Qy4rT$tead{zl~q*d%02pO}SRf9Pa@#&mPCg@6rdX*G-aCEV7uf^4U%6!N&T5DO~@+Tl_uI#pK79PFzP+HAcg%WPeNxK_}y2P0OkI#06| zflg0I$%x5I#I3pvA?cj<&p1Q=L4LcYuXQ=&TL7umd{b_gIpBHoq{8M8ux}0f1ZXme z2G<5k6b5M_s;m2&7KLuSKKK4iL-KjLiiir>oz~yJ3xDpZHG%M3wZiH0liO|>QJ+u9 z$7%shq?WlltLnD0N3<&E-7S*QRA5EL;783KoDR>*qh=4{TNt?>PK`k@kf>mhR0u8I z?%a$e3n3v0&yBDLf_6Fc*q3vWSbpI`3UBv)oGf#vZDP>VVk+k!u8LJ2%Jg+40K-^c z7E0u+tEGQ#S3r#ZwbvwB0hU|nAHkJGml!=*{!ARpuqrqA4@7Ee zqm&q>3hG}6>DvRcJpv2GmU;L>H8vs;Kk`krt#MXRNH!vBn+Dp`zT8OqwjOtEK$-DD zL3X38COaxvABXMpvo>XtlTi2-;i<-%px>8gmaC?EO#CW^CbeYX2nndkB!0Qw^m{`F zlYs+7GN3 zka8V<23xzq;-=3Y-}SPTVupMvmF?THO>OriZl8YLCCYRl64B zR?hHa{)q5$Iv}5~Mzk?tn@G1$RGyPH0w@Yi!Fvn|>E>K;lt;HXm0AHMJnhPR&!RW7gH(7j#n z83Z>%&(QBc`BGW#|@Necx4#r=IG#~n$Y))Q3nSnR- zdt2Y*Ixn?M&t$qU(@bFOAx7n8JXAC}uC9C@=p_`E4_TyF<9wjSR|aQr2< z7)*=GsHHALK+1z|kI9N0o4cyoDiw3x)10Xo8|-e%u%&2#f@0*ggev`}WBnxShlN(Hp!|&3+2XZnaa<*I#Ti{HJg|6CL_!j(yzXy+_9Zlatp5G`G zy>xA&XSl20$wv*UvB9qY-iD)0@lyMA9ub0v=SNTh=dG-DGa`!#j4PDO2k8;BL|{HW z3Ih`+}A6Z=&ZT{dvrkq!Y&Ui98N%{U&} zK4=A7FaHg%JA-Qq!uPCN-amPTdcc>uQZH9Yv&S=0u6Es4y*?m-8vLy3hP%$$9@>sg zxy>PLG${#h90^xcUUcvaw>SEHSe>y;IOcs(tYC=qqn6;VgF0KSmz z1DdzI#L;32qpbWbH#UD*&;!m58dFi!p6X!Ky~#ZmMVUQ(5vAH>tumS=5z)KUxD1*e(<##h?FUi zT#l@NWV<>@xi&OQMc4GQJAU#jwOgE0*VlRV$=;Ok`zdrHzqqMj2LHMr9$MOn-Dk$E zk-G|>Pw>&->IS4_g-FRRV7T-Hl%QuL-nbCab}S5VETrIgLSMd?E96?Ny}JwvDUK2d zg;gPl(5S7Y+c757T#B_}!S<0Zm)B9D*FKMBClA3@n{ ze+W?TIAxfxNMvY$7x0QafX02010w$gIIIm>uh`?qwH2R>0hTYSg!d~`MF@C{{Tba_#>XF z25sV)Iq0yMcyhpGM73n&Mc9Hn+=#+Kz&eYI>O}i>0s_!-?-2kwGz&JjQMEOPqM-J! zE`CVbAyS>R9*Xtf8Vohr(VT;Ol{bugW`P-!e{%c?PQ1J8SqDXAAop% zY)T|ve+MLDxkP$>IeETJGh^g&h)Kzyp(+V#sQK+n-GQ-4%dB9RRs1cbq7-iOXBt13 z*UStEIFAuvHDc3ALxJ*MXQNqD3h9>XiH_OH4p{BCQg3Ik!CX=$$1Nn5`+$yUPAPal zsM#%m*X?sm5vkmCO&g_(eO;+}X=o?UcD?8Ie7IA7DXAWb2U*EIyjo5yAUQ`^##1t! zUHBeeBv1(0KaCOB@c_S`#QxdwpseW63Fj1kY|Hcg_5QNEgS?wPM4634`biSpBFsyt zJzcOa$x$giR5bYecXw(a7acZb(QS&f-tk}1)ZSuNn-8$RZSdLd=U$!sCFoRa33r}R z6+;sHX~eD)6b9g+mnO&3+YH7GYYuS5V@Tj8Ky5=koC;1|#9b@-g0M z*FFz$Iyl$C7tCW1vmDUyI@~!O&6L{#=7q#O}@{F(@$OeQX7mqiO%wgM_?PDaKybV zJ_tTUzOBVz!4K%FWWhuKwrjZgg&EW_sx-6J$Dx%?9K zP|F>RAbbu4ADl@lB?Ol!`tBR07;;to;b~Wpz2S{mttj78RtNiGk zUx+<83!u}Glt_FObX0*-wD4kBjfsh59j?74Xs30Po}-$fH%l@xO*{rsml?kgq|=7E z$?F<)2pf3m4!L<;7RK13ZD-7pq5>3|ClooN9Z}Nc=>G-EoDKX)`}bpbei-gdo}5&4 zV>zAzT2FZ9FESyl8|TmU*@j{K&`}8Ti`LA@w^Y+JU{Y#d~BMRdfCbh{QO`25hw z#iP$}43!FvBPHx2BBY5J?Zaa7agsJui$@;TQO6R_+*qkzWQ7a%0E zNE*}%XCiVElm)@l(OX3W#L4mt4ADFp5riQY`Ho@>XD9(}@eWq3hbA$XK7M`c;hRm%8-rU~#QkUxwu0wbtAbFBnhpt&l5x>tWp#|Gg43N)0U9 zq1wX<1zqWbI;ZaiNZ-lB*+osXIDs`H3%wQAie-}e@TR*9@;GjPr8?BB-5;G zfXO*1If=mKRfvE#53UA*5ilJ=YdK|3mNa&!yecy}=x|j2_rRRVl!>|{O%y2aXDeZS zb32z%mS&}0l&u28Z=yKWcP(c;k7^FXeh+2_dFDKS8q#5;p}5R#boh~X-H}NSEg3rh zSkmBL{%GIX4Up=hpQG_*bE3=#sw4hTudk#C9dOGKzJJ>%aKA_+6ig^sfe}Tx1QRyf zq3Su^hhYXxyVQl7YH<^I)}_U(0;Od7tE*+baA0WlW+eME`ztW3$Z z3aBl7v5{fA4@@!Sw0EEk9I6T`jdg(yQl-N%*jis-NpJr`y~BaciFhVrw%wc5$Xu%j z^ckj)2MXD?t8=*&cTMGayq z00CPu%`^$(KS}PIdj}z;Uz;EXG3i>z4HTO$3}LQ9I-$6-YwJljLQe)40HNoH zCnTa2arE42eLU+a+}D%2#Sz<_8_=v~6#Y%WfmJNSGB_t&sfkWyU;*Qmk~35bMuRrq zSoe<=tc+brf*CMH*8zlKB~vEbJW=un zB;pPSMC{ zZi+cCS``OE+KM=pi$p07jmSkw6`e1KjHjLS7~)(A*iin8JMT%cstJVMHCWud%7L@C z6(x~URG1;~z$C9|vxtUldh&uOxEp~Zu_S<-Mhqca++1FZk74&xHmPuQbBk8kDq@WM z4M4${up8xbCnRmxv`s*fvt}o$sI|78`6U?a;VY)M4?a(aL^>u>3nc;%ZvBgOsaPyJ z9d5mXhds&Exv)99*Hu$ds&p}|3fK{o5w6R=`C#QUEgb>K`cCTfROW?v8;n?7d_E;V zKoY=QLVFKCSyuH=6{wqH&H%C=Ag%wRniX=qM1=WD8dyz|kLbmNm5MB5%ebh3yYmV$ z>4~F%z+ExA0@|Pd3!2`otG-J3)t1T%b!A=<$+@5Xjb&G554n50YoWe)*N!=L^VV)M zQ6Y0Ib_ET_6D10gUZ2K?)c?KLas#-7(+{U zmG}WP!C~~>A$5;P6jL-!KsyBw7KJYe($uaG7eD0+&ipyuc|S4TPs8VoV*BX5!uw#4 zJu|E^?l-wHNE1wToT0NaFq$!5eDL-40)Ana)KvBZy0u**A}BVTz`!{neu{vcc9hIftJEs9U=p}zj)%-WUFZ=3CbgqM(3C*|dQ8d~WXfarH;N+>9vs8iI#!IAz-+tGA%S@7+qCC&gB( z7C}{r@#8HPDYK8VPe{Zr{McTR2mV&CuK*RD=S_bY1>XxeYJ}%lFR(RK9)A^JZ7 z15VvCE|adscou4(;Vu-WYyP@1nuIbaX}s9am`rYL1tz;9he6%}Tet5uIr}nD3wsG_ zt4=q{lTXe-HBlx}_!xfjW576y;)d3_bYL5f8LVK?0bZhB780@Sy?0x-VdDOgc_nRi zR*$$jstf8*&YTofRQ)A6USa82_1lgNw~`W>+|QB^;o)a`V;$TbG|vplAD%#d-`Oz= z)9y(7TU(aPT)_ezi0ZX^h)4j%BNBwVtQ_V!h=@X>FH8C|7u+C?J6Hf5;@re9S7}qC z@HW!P2h{i{9t(Hn2Y8vLHD%q?g;wln+LJ7cv@1x#7z;;HR545Wv3E0vz(Il~|S(q8z%5%mUnV_>;* zO9UJLk6?m5(zn~?BRH(q|38!k0AUCIKgx>Fw?MnW|KPHU32bLtt=rZHjEy(jP07eH zTY#5_hru>|lK&;05D-T!EtJ%2YHGrz8ZOAf%*@Qx`!l@%zy*pkF$r*lA+no|@Gw3x zal3Y`%Ld^NX2;;JK!UX2W^zo-qTF&{KUaAH_AkoIRAl$#6g&7 z`sy#j|I$|(IFw~83MRH|LjO%)fLh)vLLCM@rFVG-tE;9txhNo97^G&Wvgc1AhaK@`Cnu)X7FedlBRw-&WmGO(EJQ5RPoXQuT*vak=b&evN>3 z0kjWhpu9=*jimA_DZczZnh{2R?8BX3PAwe^YOEom?%VAI%R5gtloZY0c0=}$5*PE_rKru zh8?^bRVL^RPS3Rnvbkzr0~k$f$KJ;a95-mem~*gqT1BJx-y}cYw!|v=5F^pwT_!c% zsBQI}{uSYg2;_&}?o!d8S7pLzDT{NFPU*p3m)#MZh*OkyIttB9E3(5n)kTYCpP^Sx z)t_C{Ejpjua_gCiaTX~;!Ua3DaWB>%S0gkQeSM?vOw;YtZpgj1iH6Y92a1ih{^P{C zhZevtA*|z3u=74Un3D~w$Pz^rg!lcR68o@VRt(fWi zD)O*ps=$#NVTep;gZu;pn%+}F&loPOYq$PZhF5pXT^XcI#keYUN-6(;!p14tW^p># zBhH#o@i<_)*aT`p#}WPr~6R+%gm}m1;^4Yz@L#w*pD?;qz1t!ZS`~ z(WAsXR`?7h32~a|QpheSuOKqm{M6juk?f>hr?<1^Xy9jF;t~JAK-SDxup2g)NsX zOnWDAIBHRADqBu4#4we{sD2_h(%VsQD(gnzyiBy|ro!Jov$*~HT-mo9se|tgD`^3Z zfXs_@`7p?EF9A9>+#cuS%ENLz-@ZstBBS94bZ|&~zEnvrZ3E&A^a$TOeRK)0Z%44! zA0ud4ns0dTs4np5Xkb~Z7d&h@)Y;Dp1J~imp(u8_lBOY39q{(%)96Jc&(|5J(bB4b zrwFy1N)MVNo=@(`XH$MYHr0z^fI3R=E}sHNyBn|MoxYbD8Z3YPD1BgE-v-z4JQq#{ z+`IQKCkDCNSA5ZSvcnAy>^1zClVvBAOJH_y{bL(M&!b!`Y4y-i`h*lBYxcmhP4~#O z1MKXW7lU4ZQ>$5cRa5xGklaV43V#uCjSh5}i%(>i9bq-zF68mXuoN4(G`?JB#B2d} zUDjin2~F`%JBAX`nzunml^I8L-~SJL?-X8H*DdPC){Jd? z#^m^Khu&q4>!?le9 z5Za->fPQ+8Z>$$i{ftIVckXzeJZ?Ok!CZ3Gi4?r|IdNS%DEaXN_hpye>%Q}awc1Zm zXj_f4bdgi5#OcGw&OvF|Ub#Prs(q};?#H;7t0s(}g>cYuK_ISrJnZ$>gP08n%Ol{K zsJWzm%xqRLF_SnS>E;1zO{LJj2J&MFaDo9BwVA|fi4SvRKa5(D!>_3u>1r!Hh`)@s zd3zD-^=mRb>W5KuVp}G{t#%gs3zv!f6zS%}@$vPlK1j942NDJC_Lo(?JEKHo`58T? z!yEBN>A}rFfrCi3LSzqTf!S9Nj!WZaO!+HF7HZ!wTPdo=@MquDP9Tyc5!3)AQ z!$afR?~*5*k=L*C^4F2@pbLX{_L=5NrM5b=?WJ)|=(TsLZ->i84+!%#~MRT z+SjpjhQ@=%^a(|Suzp5=>_o-K!JoAVi$;>@0`7*KXL=)1-FwbGEu_dXYc)614DvS> zcz`+X)uQHnT9=IYP$BM&-$OJtGh`2K71w`KBAw%l@O+rX>Q%hEB-X zNcM!dIPHi0Ake;h7G_mxmp6IFwx9riQyn)YB=tc;XXvDkBt+r~T2a+cJKHu zR{%-`w2JSqL0oo0a5CZuGhQ;0ko-ACF>@FToVt^V;sX-von52)G!i_pCl-Sms z$OB7o*3(?U&BXHLb*4P&jl77O3?u^L(~Y)1%{7ZZ47lnX?ljSP#PGRCu})UzQMIk| zLFO=9TxS60(B!rJ1y*Rb>%v^4qwpw2L}KmZujsEwaUJmPXDbdzdNfvX^oOU5*WCot zq++5-1rLl5gr37|-k|^mF4ToN)vWB57_)JDe(Dy22K<1~6oAVSrBYk74|;0$*I>p5 zC?wR3VaKyAoj<#TrB9urjJM)yVzg&52-Dshw(8nL6M2OP(&FGt;*O(3djKb#bHd$S zE271{C6-81B4L~Zu}NaRT8X>qpSW6I6O83_1(8&+tdd-g{o;^ZBS+?Q=T=O&rkiE}~B{sk}+aZDO1=fRBuK9rooXMDH=1 zK&KgUgRi(E<6n)UEXkv{;G2#msW_Ud+)iM| zlm2au;O%71L)(<<>=26;>e~TEeI>nN@P|bSc zP?jj>(P3=mZ9G}gl3`2>*>Az8gS8cBu-_Sq84}7oDxVX>nx$dvvsZi+aCJh*IDrj{ zd|7UtIT}t}=7%iioJwLluO)3$(fp+Wd;Sx+qr-GLBnLJn94z4TMwT= zPDRZ)IOGcn*{T59{W5Z8d{ML`7U3wy{NJ?N&G-^t{5Xa5-RWq%i4;fi4wcWcv_p_j zrY`|L#t~X2Fnlh_rU-2-;5B01FK5|t@FkE6=ZTPnpR&cCBn{=#gvPP(T{4XkX%GCK zM{P(6S;9%V7Xjl{QNK}DbJyfYI+^J@N7(zLe~%Y6U^o(aI13nxHd@)oEQ*xf_Ad)@ z8>5rfo>Qdac3cE8eq*uCeKJ$9uvNpZx2su8WsaN2!4@}mNf9koq)2?F7PW2?<5oVj zq*-rXLpzRI?F*dE#%dSc>WXco|8`gi1ZRk4_IPC7UFIIUEhWqF*$w?Y7lc|Bln)Wp zJXaJ6(EE@(r9haXcXfd>H9LD?#+{WumDPsyREe;9q_)y*;#y%y+--V+YJ!~g`B9#g z_JtOuoMfac(0(xE^zq;ZFP|0d*NQX9Q+Def{Pg#!j4~@|M8jaKfYRs4gtF@EVz3)p zqW=wZAARCrt@q>Zk%X{$;46863m-*(5QURBDd__lQDaTp!96nnE;F05k$wQbb^fA+ zt-AwM+bCP0*#O$Oh?3i{nq6R>YFGYT1tj+S;qmB03@kawnsd`88h zs#(<*2Nw4pWd$fsAoN>Oy?lwqF8&K;iUd6;K#oz2+qS-Rhf<)01FJJ~F*(2qa{JvI zmRk4@?ntV=A(WB8jObLQzZ2mZe$i5_KOXbWrwHxcd zenAU38DwNQrMZ!!&6dzc`qu}K4UGNuK71jdJ~dl@Lr(4@k69@!?!pTQh1@$(s@G|z zFCquek*%(f_WmO17ZOT(>nzD?OV9daaiS<-D#&! zI=M?CjQ;K9BMz`*c6!{uzqCXE*sU8aYL+^ggpk{nM^>z?2Zw;zvi}0|ks5~2|GV>AB+09w1tyF$2(w-?;mo{{HX6 zIy*?9tU)_x1NP zC#*X=*T2HycmjuQZi{^*!0^lncM_N+siEx^-yJS!FC$g0OK~y<2t#WgP2WyoL{*n{ zTp>1;qgcJng=Mz(f%HwpzHLW2m_?H|R|6}_xJV>2cW|`N>HkCD1r&%v$dN@D}BQPo|BRVjsiLq0IWXcpvyZQ znR|;ig_$E4$bh$J4t!iriIj^2ewm8&C6>SXvg=)3(gCKfipiw4Wc_y29O*SRV|!m5 z_qK?zh+@yE^LhA?>HgYXJUv5cK*Ev@GTLAyzhk{&g!El<1gxh+-m)zMKW6%^emkdH z&rAI66GR+YS~qk({8T%=11ahf(^(61cv~#qM&UNZ$X}nC<6rjj>uh$Nek>PTZNWZ0 zW(a&Ujfq%MqYw~C*TT!{PifH-JEIqyTRyl`?so1PCC0m`v^V_L_7Udfw4_x5Y&S+v z72u(l&*6XwtwuFj#9Pd-U=a@c5&2^bH=+6W^$dX7K?XlP>aXBNG7}Py1-Qb9d%A3L zkvLc8JgT(7))x&$BdlWpDYnJ%JsxL7nrYPLy|zw_E&H{Ntt8+%StF8GDPF9^%Dl>VTpSn;ZKm>d%91AHWkcAOfqQCu&&^$ngg}%k^=| zA@`KaOX!mM9o@L^fxNFm;NGaJ$tJ`}n#hf%68imt>UEI#IF(y2x)|^2fw3(2uQpQ!yo&%-f(#dIutp^8Da89%+ zKT~&O=TUH4F1FdGrSU=Qv6eU)^^p0T!6q+n7Nw7_78PIAIA{{Y%3~DE9cVw&7zvw& zf;-ZO%Wezx@U7LB-S9)E%Q5vNZ16FYug34Uzb3g56S{5R8^6)d^pNbt?Kssf1N|=~ zuxbyUjR}(r^`lpRgvXB?n8NcefH{(?SpnR-f{TBDMsJpV@8Z{SEyLw=fFSRGf?)ZL zDN05T*?;Y<ev<3Rq@QQ%mw~LA= zztdPJ*UVui2dD+HZ>l?1iG^+Y(^S4>ItuyuEe@rCD8`ST}ul1O=7devWd8$V-rHV%gI)*=ur~Oc=_uOUOBPY!t4#G1;70 z4|TZfQr;bQ9WwdR>PA@}-+!Z<|Hdq?C^zVmgE<6{+CcmZB8ApN)_tS5bLw2V;S1!p zDlDha_q1Ge|1Q0=AOi}3?!n~3-=Smvm;^Y%1PAuMF(dpzygthPaH7Ur!?L>SeH0?@ z_%MpY1#VWgJW!;n1*9VUK&R^YN_39HvxqwBHt`icHsIVnux8p7=LNRwd$68O4$0t0%8M|sP(>wwcRl8dP!CHBCS%gE^>Oy z&+d|xB(i~Uuh>2~=XJO%)iP>|juvx7(+z>SsaqF+CQ2}T$fbT89-dzq`OtU#2aJ

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 769/877] 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 770/877] 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 771/877] 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 772/877] 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 773/877] 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 774/877] 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 775/877] 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 776/877] 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 777/877] 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 778/877] 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 779/877] 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 780/877] 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 781/877] 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 782/877] 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 783/877] 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 784/877] 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 785/877] 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 786/877] 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 787/877] 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 788/877] 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 789/877] 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 790/877] 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 791/877] 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 792/877] 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 793/877] 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 794/877] 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 795/877] 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 796/877] 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 797/877] 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 798/877] 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 799/877] 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 800/877] 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 801/877] 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 802/877] 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 803/877] 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 804/877] 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 805/877] 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 806/877] 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 807/877] 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 808/877] 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 809/877] 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 810/877] 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 811/877] 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 812/877] 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 813/877] 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 814/877] 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 815/877] 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 816/877] 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 817/877] 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 818/877] 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 819/877] 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 820/877] 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 821/877] 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 822/877] 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 823/877] 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 824/877] 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 825/877] 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 826/877] 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 827/877] 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 828/877] 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 829/877] 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 830/877] 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 831/877] 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 832/877] 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 833/877] 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 834/877] 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 835/877] 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 836/877] 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 837/877] 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 838/877] 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 839/877] 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 840/877] 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 841/877] 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 842/877] 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 843/877] 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 844/877] 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 845/877] 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 846/877] 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 847/877] 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 848/877] 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 849/877] 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 850/877] 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 851/877] 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 852/877] 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 853/877] 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 854/877] 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 855/877] 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 856/877] 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 857/877] 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 858/877] 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 c6581358c40e4a1d600d91544b6da33e3c6efe30 Mon Sep 17 00:00:00 2001 From: hodbauer Date: Tue, 1 Aug 2023 10:09:59 +0300 Subject: [PATCH 859/877] 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 860/877] 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 861/877] 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 862/877] 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 863/877] 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 864/877] 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 865/877] 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 866/877] 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 867/877] 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 868/877] 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 869/877] 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 870/877] 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 871/877] 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 872/877] 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 873/877] 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 04b3c2c3eb5bd09fed4bfc3da047f59ecec939cc Mon Sep 17 00:00:00 2001 From: Ahmed Kotby <87335804+TheCodby@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:18:29 +0300 Subject: [PATCH 874/877] Create README.arabic.md --- README.arabic.md | 1977 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1977 insertions(+) create mode 100644 README.arabic.md diff --git a/README.arabic.md b/README.arabic.md new file mode 100644 index 000000000..719deba3c --- /dev/null +++ b/README.arabic.md @@ -0,0 +1,1977 @@ +
+ +# Node.js أفضل ممارسات + +

+ Node.js Best Practices +

+ +
+ +
+ 102 items Last update: July 19, 2023 Updated for Node 19.0.0 +
+ +
+ +[](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), [![FR](./assets/flags/FR.png)**FR**](./README.french.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), [![JA](./assets/flags/JA.png)**JA**](./README.japanese.md), [![EU](./assets/flags/EU.png)**EU**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ES**, ![HE](./assets/flags/HE.png)**HE**, ![KR](./assets/flags/KR.png)**KR** and ![TR](./assets/flags/TR.png)**TR** in progress! )](#translations) + +
+ +# 🎊 2023 edition is here! + +- **🛰 Modernized to 2023**: 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 + +- **🔖 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 + +**1. You are 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. 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 + +

+ +# 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 + +
+ + 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)
+  [2.13 Subscribe to event emitters 'error' event `#new`](#-213-subscribe-to-event-emitters-and-streams-error-event)
+ +
+ +
+ + 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 `#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)
+ +
+ +
+ + 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 + +### `📝 #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 +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 + +### `📝 #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 +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 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 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 +├─ 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 + +### `📝 #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](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 + +🔗 [**Read More: configuration best practices**](./sections/projectstructre/configguide.md) + +

+ +## ![✔] 1.5 Consider all the consequences when choosing the main framework + +### `🌟 #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) + +**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 + +### `🌟 #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 + +**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 + +### `📝 #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! + +🔗 [**Read More: using the built-in error object**](./sections/errorhandling/useonlythebuiltinerror.md) + +

+ +## ![✔] 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 + +🔗 [**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 + +### `📝 #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 + +🔗 [**Read More: using a mature logger**](./sections/errorhandling/usematurelogger.md) + +

+ +## ![✔] 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 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 + +🔗 [**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 + +### `📝 #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 + +🔗 [**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 + +### `🌟 #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 +`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) + +

+ +## ![✔] 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

+ +# `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 + +### `📝 #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 + +

+ +## ![✔] 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 + +### `📝 #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 + +### 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:** 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 + +🔗[**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 + +### `🌟 #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 + +**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 + +### `🌟 #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 + +**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 + +### `🌟 #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 + +**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 + +### `🌟 #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) + +**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 + +### `🌟 #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 + +**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 + +### `🌟 #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 + +**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 + +### `🌟 #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) + +**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 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 + +🔗 [**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 + +### `📝 #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 + +🔗 [**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 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 + +### `📝 #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 + +🔗 [**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 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. + +🔗 [**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 + +### `🌟 #new` + + + +**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 + +### `🌟 #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 + +**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 + +### `🌟 #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. + +**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](./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)) + +

+ +## 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

🖋
hodbauer
hodbauer

🌍
+ + + + + + +### 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 a01c67fefc733ce22f335cd898a70e6e92f8f405 Mon Sep 17 00:00:00 2001 From: Ahmed Kotby <87335804+TheCodby@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:23:36 +0300 Subject: [PATCH 875/877] 16x16 ar flag --- assets/flags/ar.png | Bin 0 -> 314 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/flags/ar.png diff --git a/assets/flags/ar.png b/assets/flags/ar.png new file mode 100644 index 0000000000000000000000000000000000000000..a5bb1c4e2c522b9bbf8ded545d5c1973fe1be8e4 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#X!0Ygc<#+)i{6*$r9Iy zlHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSj(fT|hFJ6_CrD%*h>(ym{42}< zyL^+c#nAzy<_rjB#x9+GN<)wJ_{qGk7>9F)=Yg=z;1B zrZo${oj=|4u)J92u(qNdTibW@k5UbfTHn{4;b|9Qk$5+wSa6HvVb=09J0WXkg}F#L135UbzyZ7a}A44$rjF6*2UngH40 BY%u@; literal 0 HcmV?d00001 From 58fbb65088a6f2e1f936d47bfdb79d0807be3b29 Mon Sep 17 00:00:00 2001 From: Ahmed Kotby <87335804+TheCodby@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:25:09 +0300 Subject: [PATCH 876/877] 16*16 AR flag --- assets/flags/AR.png | Bin 0 -> 314 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/flags/AR.png diff --git a/assets/flags/AR.png b/assets/flags/AR.png new file mode 100644 index 0000000000000000000000000000000000000000..a5bb1c4e2c522b9bbf8ded545d5c1973fe1be8e4 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#X!0Ygc<#+)i{6*$r9Iy zlHmNblJdl&REF~Ma=pyF?Be9af>gcyqV(DCY@~pSj(fT|hFJ6_CrD%*h>(ym{42}< zyL^+c#nAzy<_rjB#x9+GN<)wJ_{qGk7>9F)=Yg=z;1B zrZo${oj=|4u)J92u(qNdTibW@k5UbfTHn{4;b|9Qk$5+wSa6HvVb=09J0WXkg}F#L135UbzyZ7a}A44$rjF6*2UngH40 BY%u@; literal 0 HcmV?d00001 From 260d6cc5345dbc4f1055260dcacd91dcfa252918 Mon Sep 17 00:00:00 2001 From: Ahmed Kotby <87335804+TheCodby@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:26:14 +0300 Subject: [PATCH 877/877] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 04a5a97fe..e3139767d 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), [![FR](./assets/flags/FR.png)**FR**](./README.french.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), [![JA](./assets/flags/JA.png)**JA**](./README.japanese.md), [![EU](./assets/flags/EU.png)**EU**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ES**, ![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), [![FR](./assets/flags/FR.png)**FR**](./README.french.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), [![JA](./assets/flags/JA.png)**JA**](./README.japanese.md), [![EU](./assets/flags/EU.png)**EU**](./README.basque.md) [(![ES](./assets/flags/ES.png)**ES**, ![HE](./assets/flags/HE.png)**HE**, ![KR](./assets/flags/KR.png)**KR** and ![TR](./assets/flags/TR.png)**TR** ![AR](./assets/flags/AR.png)**AR** in progress! )](#translations)
@@ -1630,6 +1630,7 @@ All translations are contributed by the community. We will be happy to get any h - ![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)) +- ![AR](./assets/flags/AR.png) [Arabic](https://github.com/goldbergyoni/nodebestpractices/blob/spanish-translation/README.arabic.md)