Clean Code in JavaScript - James Padolsey - E-Book

Clean Code in JavaScript E-Book

James Padolsey

0,0
32,36 €

-100%
Sammeln Sie Punkte in unserem Gutscheinprogramm und kaufen Sie E-Books und Hörbücher mit bis zu 100% Rabatt.

Mehr erfahren.
Beschreibung

Get the most out of JavaScript for building web applications through a series of patterns, techniques, and case studies for clean coding




Key Features



  • Write maintainable JS code using internal abstraction, well-written tests, and well-documented code


  • Understand the agents of clean coding like SOLID principles, OOP, and functional programming


  • Explore solutions to tackle common JavaScript challenges in building UIs, managing APIs, and writing states



Book Description



Building robust apps starts with creating clean code. In this book, you'll explore techniques for doing this by learning everything from the basics of JavaScript through to the practices of clean code. You'll write functional, intuitive, and maintainable code while also understanding how your code affects the end user and the wider community.






The book starts with popular clean-coding principles such as SOLID, and the Law of Demeter (LoD), along with highlighting the enemies of writing clean code such as cargo culting and over-management. You'll then delve into JavaScript, understanding the more complex aspects of the language. Next, you'll create meaningful abstractions using design patterns, such as the Class Pattern and the Revealing Module Pattern. You'll explore real-world challenges such as DOM reconciliation, state management, dependency management, and security, both within browser and server environments. Later, you'll cover tooling and testing methodologies and the importance of documenting code. Finally, the book will focus on advocacy and good communication for improving code cleanliness within teams or workplaces, along with covering a case study for clean coding.






By the end of this book, you'll be well-versed with JavaScript and have learned how to create clean abstractions, test them, and communicate about them via documentation.




What you will learn



  • Understand the true purpose of code and the problems it solves for your end-users and colleagues


  • Discover the tenets and enemies of clean code considering the effects of cultural and syntactic conventions


  • Use modern JavaScript syntax and design patterns to craft intuitive abstractions


  • Maintain code quality within your team via wise adoption of tooling and advocating best practices


  • Learn the modern ecosystem of JavaScript and its challenges like DOM reconciliation and state management


  • Express the behavior of your code both within tests and via various forms of documentation



Who this book is for



This book is for anyone who writes JavaScript, professionally or otherwise. As this book does not relate specifically to any particular framework or environment, no prior experience of any JavaScript web framework is required. Some knowledge of programming is assumed to understand the concepts covered in the book more effectively.

Das E-Book können Sie in Legimi-Apps oder einer beliebigen App lesen, die das folgende Format unterstützen:

EPUB

Seitenzahl: 748

Veröffentlichungsjahr: 2020

Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Clean Code in JavaScript

 

 

 

 

Develop reliable, maintainable, and robust JavaScript

 

 

 

 

 

 

 

 

 

 

 

James Padolsey

 

 

 

 

 

 

 

 

 

 

 

 

 

BIRMINGHAM - MUMBAI

Clean Code in JavaScript

Copyright © 2020 Packt Publishing

All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without the prior written permission of the publisher, except in the case of brief quotations embedded in critical articles or reviews.

Every effort has been made in the preparation of this book to ensure the accuracy of the information presented. However, the information contained in this book is sold without warranty, either express or implied. Neither the author, nor Packt Publishing or its dealers and distributors, will be held liable for any damages caused or alleged to have been caused directly or indirectly by this book.

Packt Publishing has endeavored to provide trademark information about all of the companies and products mentioned in this book by the appropriate use of capitals. However, Packt Publishing cannot guarantee the accuracy of this information.

Commissioning Editor: Pavan RamchandaniAcquisition Editor: Ashitosh GuptaContent Development Editor: Akhil NairSenior Editor: Martin WhittemoreTechnical Editor: Suwarna PatilCopy Editor: Safis EditingProject Coordinator: Kinjal BariProofreader: Safis EditingIndexer: Manju ArasanProduction Designer: Deepika Naik

First published: January 2020

Production reference: 1170120

Published by Packt Publishing Ltd. Livery Place 35 Livery Street Birmingham B3 2PB, UK.

ISBN 978-1-78995-764-8

www.packt.com

 

Packt.com

Subscribe to our online digital library for full access to over 7,000 books and videos, as well as industry leading tools to help you plan your personal development and advance your career. For more information, please visit our website.

Why subscribe?

Spend less time learning and more time coding with practical eBooks and Videos from over 4,000 industry professionals

Improve your learning with Skill Plans built especially for you

Get a free eBook or video every month

Fully searchable for easy access to vital information

Copy and paste, print, and bookmark content

Did you know that Packt offers eBook versions of every book published, with PDF and ePub files available? You can upgrade to the eBook version at www.packt.com and as a print book customer, you are entitled to a discount on the eBook copy. Get in touch with us at [email protected] for more details.

At www.packt.com, you can also read a collection of free technical articles, sign up for a range of free newsletters, and receive exclusive discounts and offers on Packt books and eBooks. 

Contributors

About the author

James Padolsey is a passionate JavaScript and UI engineer with over 12 years' experience. James began his journey into JavaScript as a teenager, teaching himself how to build websites for school and small freelance projects. In the early years, he was a prolific blogger, sharing his unique solutions to common problems in the domains of jQuery, JavaScript, and the DOM. He later contributed to the jQuery library itself and authored a chapter within the jQuery Cookbook published by O'Reilly Media. Over subsequent years, James has been exposed to many unique software projects in his employment at Stripe, Twitter, and Facebook, informing his philosophy on what clean coding truly means in the ever-changing ecosystem of JavaScript.

I'd like to thank the following individuals for their technical insight in the domain of JavaScript: Paul Irish, Alex Sexton, Axel Rauschmayer, John Resig, John Hann, Mathias Bynens, Ana Tudor, Steven Levithan, Juriy Zaytsev, Peter van der Zee, Addy Osmani, Jake Archibald, Dave Methvin, and Lea Verou. I would like to especially thank my family, including Victoria, Henry, George, Alice, and Lucy, and my friends Erik Lundin, Owen Barnes, and Anna Stark.

 

 

 

About the reviewers

Derrek Landauer teaches middle school math and mentors students in an Air Force Research Lab rocketry science program. He earned a bachelor of science in electrical engineering from the University of Texas at El Paso in 2011. His work history spans industry and academia. While attending school, he was involved in defense research projects and was also a lab instructor. He later spent a couple of years managing the network and server infrastructure across four facilities for a subsidiary of a Fortune 100 company. His software development background ranges from programming microprocessors and operating systems to full stack web development.

 

Dobrin Ganev is a software developer with years of experience working in various development environments, ranging from finance to business process management. In recent years, he has focused on geospatial development and data analytics using JavaScript, Python, Scala, and R. He has extensive knowledge of open source geospatial software and the ESRI platform. He is also skilled in Node.js, React.js, and GraphQL. Dobrin recently authored a video course entitled Hands-On Full Stack Web Development with GraphQL and React, published by Packt.

 

 

Packt is searching for authors like you

If you're interested in becoming an author for Packt, please visit authors.packtpub.com and apply today. We have worked with thousands of developers and tech professionals, just like you, to help them share their insight with the global tech community. You can make a general application, apply for a specific hot topic that we are recruiting an author for, or submit your own idea.

Table of Contents

Title Page

Copyright and Credits

Clean Code in JavaScript

About Packt

Why subscribe?

Contributors

About the author

About the reviewers

Packt is searching for authors like you

Preface

Who this book is for

What this book covers

To get the most out of this book

Download the example code files

Download the color images

Conventions used

Get in touch

Reviews

Section 1: What is Clean Code Anyway?

Setting the Scene

Why we write code

Code as intent

Who is the user?

What is the problem? 

Truly understanding the problem domain

Writing code for humans

Communicating intent

Readability

Meaningful abstractions

The tower of abstraction

The layers of clean code

Summary

The Tenets of Clean Code

Reliability

Correctness

Stability

Resilience

Efficiency

Time

Space

Efficiency's effects

Maintainability

Adaptability

Familiarity

Usability

User stories

Intuitive design

Accessibility

Summary

The Enemies of Clean Code

Enemy #1 – JavaScript

Enemy #2 – management

Pressure to ship 

Bad metrics

Lack of ownership

Enemy #3 – Self

Showing off with syntax

Stubborn opinions

Imposter syndrome

Enemy #4 – The cargo cult

Cargo culting code

Cargo culting tools and libraries

Summary

SOLID and Other Principles

The Law of Demeter

SOLID

Single responsibility principle

Open–closed principle

Liskov substitution principle

Interface segregation principle

Dependency inversion principle

The abstraction principle

Over-abstraction

Under-abstraction

Balanced abstraction

Functional programming principles

Functional purity

Immutability

Summary

Naming Things Is Hard

What's in a name?

Purpose

Concept

Contract

Naming anti-patterns

Needlessly short names

Needlessly exotic names

Needlessly long names

Consistency and hierarchy

Techniques and considerations

Hungarian notation

Naming and abstracting functions

Three bad names

Summary

Section 2: JavaScript and Its Bits

Primitive and Built-In Types

Primitive types 

Immutability of primitives

Primitive wrappers

The falsy primitives

Number

String

Boolean

BigInt

Symbol

null

undefined

Objects

Property names

Property descriptors

Map and WeakMap

The prototype

When and how to use objects

Functions

Syntactic context

Function bindings and this

Execution context

super

new.target

arguments

Function names

Function declarations

Function expressions

Arrow functions

Immediately Invoked Function Expressions

Method definitions

Async functions

Generator functions

Arrays and iterables

Array-like objects

Set and WeakSet

Iterable protocol

RegExp

Regular expression 101

RegExp flags

Methods accepting RegExp

RegExp methods and lastIndex

Stickiness

Summary

Dynamic Typing

Detection

The typeof operator

Type-detecting techniques

Detecting Booleans

Detecting numbers

Detecting strings

Detecting undefined

Detecting null

Detecting null or undefined

Detecting arrays

Detecting instances

Detecting plain objects

Conversion, coercion, and casting

Converting into a Boolean

Converting into a String

Converting into a Number

Converting into a primitive

Summary

Operators

What is an operator?

Operator arity

Operator function

Operator precedence and associativity 

Arithmetic and numeric operators

The addition operator

Both operands are numbers

Both operands are strings

One operand is a string

One operand is a non-primitive

Conclusion – know your operands!

The subtraction operator

The division operator

The multiplication operator

The remainder operator

The exponentiation operator

The unary plus operator

The unary minus operator

Logical operators

The logical NOT operator

The logical AND operator

The logical OR operator

Comparative operators

Abstract equality and inequality

Strict equality and inequality

Greater than and less than

Lexicographic comparison

Numeric comparison

The instanceof operator

The in operator

Assignment operators

Increment and decrement (prefix and postfix) operators

Prefix increment/decrement

Postfix increment/decrement

Destructuring assignment

Property access operators

Direct  property access

Computed property access

Other operators and syntax

The delete operator

The void operator

The new operator

The spread syntax

The comma operator

Grouping

Bitwise operators

Summary

Parts of Syntax and Scope

Expressions, statements, and blocks

Expressions

Statements

Forming statements with semicolons

Blocks

Scopes and declarations

Variable declarations

Let declarations

Const declarations

Function declarations

Closures

Summary

Control Flow

What is control flow?

Imperative versus declarative programming

The movement of control

Invocation

Returning

Yielding

Yielding to a yield

Complexity of yielding

Breaking

Continuing

Throwing

Statements of control flow

The if statement

The for statement

Conventional for

for...in

for...of

The while statement

The do...while statement

The switch statement

Breaking and fallthrough

Returning from a switch directly

Case blocks

Multivariant conditions

Handling cyclomatic complexity

Simplifying conditional spaghetti

Asynchronous control flow

The Event Loop

Native asynchronous APIs

Callbacks

Event subscribing/emitting

Promises

async and await

Summary

Section 3: Crafting Abstractions

Design Patterns

The perspective of a designer

Architectural design patterns

MVC

A working example of MVC

MVVM

MV* and the nature of software

JavaScript modules

Modular design patterns

Constructor patterns

When to use the Constructor pattern

Inheritance with the Constructor pattern

The Class pattern

When to use the Class pattern

Static methods

Public and private fields

Extending classes

Mixing-in classes

Accessing a super-class

The Prototype pattern

When to use the Prototype pattern

The Revealing Module pattern

The Conventional Module pattern

When to use the Conventional Module pattern

The Singleton Class pattern

When to use the Singleton Class pattern

Planning and harmony

Summary

Real-World Challenges

The DOM and single-page applications

DOM binding and reconciliation

DOM reconciliation

React's approach

Messaging and data propagation

Frontend routing

Dependency management

Module definition – then and now

npm and package.json

Bundling and serving 

Security

Cross-Site Scripting

Content Security Policy

Subresource Integrity

Cross-Site Request Forgery

Other security vulnerabilities

Summary

Section 4: Testing and Tooling

The Landscape of Testing

What is a test?

The simple assertion

Many moving parts

Types of testing

Unit testing

Integration testing

E2E and functional testing

Test-Driven Development

Summary

Writing Clean Tests

Testing the right thing 

Writing intuitive assertions

Creating clear hierarchies

Providing final clarity

Creating clean directory structures

Summary

Tools for Cleaner Code

Linters and formatters

Static typing

E2E testing tools

Automated builds and CI

Summary

Section 5: Collaboration and Making Changes

Documenting Your Code

Aspects of clean documentation

Concept

Specification

Instruction

Usability

Documentation is everywhere

Writing for non-technical audiences

Summary

Other Peoples' Code

Inheriting code

Exploring and understanding

Making a flowchart

Finding structure and observing history

Stepping through the code

Asserting your assumptions

Making changes

Minimally invasive surgery

Encoding changes as tests

Dealing with third-party code

Selection and understanding

Encapsulating and adapting third-party code

Summary

Communication and Advocacy

Planning and setting requirements

Understanding user needs

Quick prototypes and PoCs

Communication strategies

Listen and respond

Explain from the user's perspective

Have small and focused communications

Ask stupid questions and have wild ideas

Pair programming and 1:1s

Identifying issues and driving change

Raising bugs

Driving systemic change

Summary

Case Study

The problem

The design

The implementation

The Plant Selection application

Creating the REST API

Creating the client-side build process

Creating the component

Summary 

Other Books You May Enjoy

Leave a review - let other readers know what you think

Preface

JavaScript is a scrappy yet graceful language that has found itself at the center of one of the greatest software shifts in history. It is now the primary programming language used to deliver user experiences on the most ubiquitous platform that exists: the web.

This huge responsibility has meant that the JavaScript language has had to grow up very quickly in a period of shifting demands. For the up-and-coming JavaScript programmer or web developer, these changes have meant that the language and its ecosystem have been increasingly complex to grasp. Nowadays, the sheer number of frameworks and libraries available is overwhelming, even to those who've been in the industry for many years.

The task of this book is to peel back the confusing layers and concepts that the world has placed atop the language to reveal its underlying nature and consider how we can use it to craft reliable and maintainable code with a focus on usability. We will begin by zooming out and considering, in a very fundamental way, why we even write code. We will discover that the code we write does not exist in a vacuum. We will explore the large and small ways in which our code drastically affects our users and fellow programmers, and discuss ways that we can accommodate their various needs.

Beyond a fundamental exploration of clean coding principles, we will deeply delve into JavaScript itself, guiding you through the language, from its most atomic syntax to its more abstract design patterns and conventions. We will also explore how we can go about documenting and testing our code in the cleanest way possible. You should come away with a solid grasp of the JavaScript language and an attuned sense of what clean code is. 

Who this book is for

This book is for anyone who has an interest in improving their JavaScript skills. Whether you are an amateur or a professional, there are aspects of this book that you will find valuable. In terms of technical knowledge, the book assumes some previous exposure to programming and at least a small amount of experience of JavaScript itself. The reader who will get the most value from this book is someone who has programmed for a number of months or years in JavaScript but has always felt weighed down by the complexity of it and is unsure of how to craft clean and bug-free JavaScript.

What this book covers

Chapter 1, Setting the Scene, asks you to consider why we write code and explores the many ways in which we communicate our intent via code. This chapter provides a firm foundation upon which you can build and adapt your understanding of clean code.

Chapter 2, The Tenets of Clean Code, uses real-world JavaScript examples to explore the four tenets of clean code: reliability, efficiency, maintainability, and usability. Each of these vital tenets serves as a foundation for the rest of the book.

Chapter 3, The Enemies of Clean Code, uncovers some of the more notorious enemies of clean code. These are the forces and dynamics that lead to the proliferation of unclean code, such as egotistic programming, bad metrics, and cargo cults.

Chapter 4, SOLID and Other Principles, explores the famous SOLID principles and uncovers their deeper meaning by tying them together with functional programming principles, the Law of Demeter, and the abstraction principle.

Chapter 5, Naming Things Is Hard, discusses one of the most challenging aspects of programming: naming things. It poses some of the specific challenges of naming and ties together a foundational naming theory with real-world naming problems and solutions.

Chapter 6, Primitive and Built-In Types, begins a deep exploration into JavaScript. This chapter details the primitive and built-in types available to the JavaScript programmer, warning against common pitfalls and sharing best practices. 

Chapter 7, Dynamic Typing, discusses JavaScript's dynamic nature, and goes over some of the challenges related to this. It explains how we can both cleanly detect and convert to various types (via explicit casting or implicit coercion).

Chapter 8, Operators, thoroughly details the operators available within JavaScript, discussing their behaviors and challenges. This includes a detailed account of every operator alongside examples, pitfalls, and best practices.

Chapter 9, Parts of Syntax and Scope, provides a more macro view of the language, highlighting the broader syntaxes and constructs available, such as statements, expressions, blocks, and scope. 

Chapter 10, Control Flow, broadly covers the concept of control flow, highlighting the crucial difference between imperative and declarative forms of programming. It then explores how we can cleanly control flow within JavaScript by utilizing control-moving mechanisms such as invoking, returning, yielding, throwing, and more.

Chapter 11, Design Patterns, broadly explores some of the more popular design patterns used in JavaScript. It describes the major architectural design patterns of MVC and MVVM, and the more modular design patterns such as the Constructor pattern, the Class pattern, the Prototype pattern, and the Revealing Module pattern.

Chapter 12, Real-World Challenges, looks at some of the more realistic problem domains within the JavaScript ecosystem and considers how they can be handled cleanly. Topics covered include the DOM and single-page applications, dependency management, and security (XSS, CSRF, and more).

Chapter 13, The Landscape of Testing, describes the broad concepts of testing software, and how these can be applied to JavaScript. It specifically explores unit testing, integration testing, E2E testing, and TDD.

Chapter 14, Writing Clean Tests, delves further into the domain of testing by advising you to author assertions and test suites in a way that is utterly clear, intuitive, representative of the problem domain, and conceptually hierarchical.

Chapter 15, Tools for Cleaner Code, briefly considers several available tools and development flows that can greatly aid us in writing and maintaining clean code. Included are topics such as linting, formatting, source control, and continuous integration.

Chapter 16, Documenting Your Code, uncovers the unique challenges of documentation. This chapter challenges you to consider all the mediums of documentation that are available and asks you to consider how we can understand and accommodate the needs and questions of individuals who may wish to utilize or maintain our code. 

Chapter 17, Other Peoples' Code, looks into the challenges of selecting, understanding, and making use of third-party code within our  JavaScript projects (such as third-party libraries, frameworks, and utilities). It also discusses methods of encapsulation that allow us to interface with third-party code in a clean and minimally invasive way.

Chapter 18, Communication and Advocacy, explores the wider project-based and interpersonal challenges inherent in the crafting and delivery of clean software. This includes a detailed inquiry into the following: planning and setting requirements, communication strategies, and identifying issues and driving change.

Chapter 19, Case Study, concludes the book with a walk-through of the development of a JavaScript project, including both client-side and server-side pieces. This chapter draws together the principles espoused within the book and affirms them by exposing you to a real-world problem domain and the development of a usable solution.

To get the most out of this book

In order to get the most out of this book, it is useful to have a basic understanding of the JavaScript language and to have some experience of atleast one platform in which JavaScript is utilized. This may include the browser or Node.js, for example. 

In order for you to execute the pieces of code shared within the book, you have a few options available:

Create an HTML file with

<script>

in which you can place any JavaScript code you wish to test. In order to observe an output visually, you can either use

alert()

or

console.log()

. In order to view values outputted via

console.log()

, you can open the development tools of the browser.

Directly open the development tools of any modern browser and directly type JavaScript expressions and statements into the JavaScript console. A guide to doing this within the Chrome browser can be found here: 

https://developers.google.com/web/tools/chrome-devtools/console/javascript

.

Create a

test.js

file and run it via Node.js or use the Node.js REPL to interactively test distinct JavaScript statements and expressions via the command line. A comprehensive guide to getting started with Node.js can be found here: 

https://nodejs.org/en/docs/guides/getting-started-guide/

.

Browser development tools are accessible within all modern browsers. The shortcuts are as follows:

In Chrome

Ctrl

+

Shift

+

J

 on Windows or

CMD

+

Shift

+

J

 on macOS

In Firefox

Ctrl

+

Shift

+

I

or

F12

on Windows and Linux, or

CMD

+

OPTION

+

I

on macOS

In IE

:

F12

on Windows

You are advised to move through the book at your own pace and conduct additional research and exploration online if you are finding a topic hard to grasp. Some especially helpful resources include the following:

Mozilla Developer Network: 

https://developer.mozilla.org/en-US/docs/Web/JavaScript

ECMAScript Language Specification: 

https://www.ecma-international.org/publications/standards/Ecma-262.htm

The book gets progressively more detailed as you advance through it, so it is natural to take a slower pace in later chapters. This may be especially true for Chapters 6-12, which cover, in great detail, the characteristics of the JavaScript language itself.

Download the example code files

You can download the example code files for this book from your account at www.packt.com. If you purchased this book elsewhere, you can visit www.packtpub.com/support and register to have the files emailed directly to you.

You can download the code files by following these steps:

Log in or register at

www.packt.com

.

Select the

Support

tab.

Click on

Code Downloads

.

Enter the name of the book in the

Search

box and follow the onscreen instructions.

Once the file is downloaded, please make sure that you unzip or extract the folder using the latest version of:

WinRAR/7-Zip for Windows

Zipeg/iZip/UnRarX for Mac

7-Zip/PeaZip for Linux

The code bundle for the book is also hosted on GitHub at https://github.com/PacktPublishing/Clean-Code-in-JavaScript. In case there's an update to the code, it will be updated on the existing GitHub repository.

We also have other code bundles from our rich catalog of books and videos available at https://github.com/PacktPublishing/. Check them out!

 

Download the color images

We also provide a PDF file that has color images of the screenshots/diagrams used in this book. You can download it here: https://static.packt-cdn.com/downloads/9781789957648_ColorImages.pdf.

Conventions used

There are a number of text conventions used throughout this book.

CodeInText: Indicates code words in text, database table names, folder names, filenames, file extensions, pathnames, dummy URLs, user input, and Twitter handles. Here is an example: "We find a publicly available package called shipping_address_validator and decide to use it."

A block of code is set as follows:

function validatePostalCode(code) { return /^[0-9]{5}(?:-[0-9]{4})?$/.test(code);}

Any command-line input or output is written as follows:

npm install --save react react-dom

Bold: Indicates a new term, an important word, or words that you see on screen. For example, words in menus or dialog boxes appear in the text like this. Here is an example: "For the purposes of our case study, the plant names only exist as their full Latin names, which includes a family (for example, Acanthaceae)."

Warnings or important notes appear like this.
Tips and tricks appear like this.

Get in touch

Feedback from our readers is always welcome.

General feedback: If you have questions about any aspect of this book, mention the book title in the subject of your message and email us at [email protected].

Errata: Although we have taken every care to ensure the accuracy of our content, mistakes do happen. If you have found a mistake in this book, we would be grateful if you would report this to us. Please visit www.packtpub.com/support/errata, selecting your book, clicking on the Errata Submission Form link, and entering the details.

Piracy: If you come across any illegal copies of our works in any form on the Internet, we would be grateful if you would provide us with the location address or website name. Please contact us at [email protected] with a link to the material.

If you are interested in becoming an author: If there is a topic that you have expertise in and you are interested in either writing or contributing to a book, please visit authors.packtpub.com.

Reviews

Please leave a review. Once you have read and used this book, why not leave a review on the site that you purchased it from? Potential readers can then see and use your unbiased opinion to make purchase decisions, we at Packt can understand what you think about our products, and our authors can see your feedback on their book. Thank you!

For more information about Packt, please visit packt.com.

Section 1: What is Clean Code Anyway?

In this section, we'll discuss the purpose of code and the tenets of it, such as clarity and maintainability. We'll also cover the very broad challenge of naming things, as well as some of the valuable questions and hazards to watch out for.

This section contains the following chapters:

Chapter 1

,

Setting the Scene

Chapter 2

The Tenets of Clean Code

Chapter 3

The Enemi

es of Clean Code

Chapter 4

SOLID and Other Principles

Chapter 5

,

Naming Things Is Hard

Setting the Scene

JavaScript was created by Brendan Eich in 1995, with the goal of being a glue language. It was intended to help web designers and amateurs easily manipulate and derive behavior from their HTML. JavaScript was able to do this via the DOM API, a set of interfaces provided by the browser that would give access to the parsed representation of HTML. Soon after this, DHTML became the popular term, referring to the more dynamic user interfaces that JavaScript enabled: everything from animated rollover button states to client-side form validation. Eventually came the rise of Ajax, which enabled communication between the client and the server. This opened up a considerable fountain of potential applications. The web, previously purely the domain of documents, was now on the way to becoming a powerhouse of processor- and memory-intensive applications:

In 1995, nobody could have predicted that JavaScript would one day be used to build complex web applications, program robots, query databases, write plugins for photo manipulation software, and be behind one of the most popular server runtimes in existence, Node.js.

In 1997, not long after its creation, JavaScript was standardized by Ecma International under the name ECMAScript, and it is still undergoing frequent changes under the TC39 committee. Most recent versions of the language have been named according to the year of their release, such as ECMAScript 2020 (ES2020). 

Due to its burgeoning capabilities, JavaScript has attracted a passionate community that drives its growth and ubiquity. And due to its considerable popularity, there are now countless different ways to do the same thing in JavaScript. There are thousands of popular frameworks, libraries, and utilities. The language too is changing on a near-constant basis in reaction to the increasing demands of its applications. This creates a great challenge: among all of this change, while being pushed and pulled in different directions, how can we know how to write the best possible code? Which frameworks should we use? What conventions should we employ? How should we test our code? How should we craft sensible abstractions?

 

To answer these questions, we need to briefly go back to basics. And that is the purpose of this chapter. We'll be discussing the following:

What the true purpose of the code is

Who our users are and what problems they have

What it means to write code for humans

Why we write code

At its simplest, we know that programming is about instructing computers, but what are we instructing them to do? And to what end? And what other purposes does code serve?

We can broadly say that code is a way of solving problems. By writing code, we are expressing a complex task or series of actions, distilling them into a singular process that can be easily utilized by a user. So we can say that the code is an expression of a problem domain. We can even say it is a form of communication, a way to relay information and intent. Understanding that code is a complex thing with many complementary purposes, such as problem-solving and communication, will enable us to use it to its fullest potential. Let's delve further into this complexity by exploring what we mean when we speak of code as a method of relaying intent.

Code as intent

We often think of code as simply a series of instructions that are executed by a computer. But in many ways, this misses the true magic of what we're doing when we write code. When we convey instructions, we are expressing our intent to the world; we are saying These are the things that I want to occur.

Humans have been conveying instructions for as long as they've been around. One example of this is a simple cooking recipe:

Cut about three hundred grams of butter (small cubes!) Take 185 grams dark chocolate Melt it with butter over a saucepan Break half dozen eggs, ideally large ones Mix them together with a few cups of sugar

Instructions like these are quite easy to understand for a human, but you'll notice they follow no strict specification. The measuring units are inconsistent, as is the punctuation and the wording. And some of the instructions are quite ambiguous and therefore open to misinterpretation by someone who hasn't cooked before:

What constitutes a large egg?

When should I consider the butter fully melted?

How dark should the dark chocolate be?

How small is a

small cube

 of butter?

What does

over a saucepan

 mean?

Humans can usually muddle through such ambiguities with their initiative and experience, but machines aren't so adept. A machine must be instructed with enough specificity to carry out every step. What we wish to communicate to a machine is our intent, that is, please do this thing, but due to the nature of machines, we must be utterly specific. Thankfully, how we choose to write these instructions is up to us; there are many programming languages and approaches, and almost all of them were created with the goal of making it easier for humans to communicate their intent in a less burdensome way.

The distance between human capability and computing capability is quickly narrowing. The advent of machine learning, natural language processing, and highly specialized programs means that machines are far more flexible in the types of instructions they can carry out. However, code will continue to be useful for some time, as it allows us to communicate in a highly specific and standardized way. With this high level of specificity and consistency, we can have more faith that our instructions will be executed as intended, every time.

Who is the user?

No meaningful conversation about programming can occur without considering the user. The user, whether they are a fellow programmer or the end user of a UI, is at the core of what we do.

Let's imagine that we are tasked with validating user-inputted shipping addresses on a website. This particular website sells medication to hospitals around the world. We're in a bit of a rush and would prefer to use something that someone else has implemented.We find a publicly available package called shipping_address_validator and decide to use it.

If we had taken the time to check the code within the package, in its postal code validation file, we would have seen this:

function validatePostalCode(code) { return /^[0-9]{5}(?:-[0-9]{4})?$/.test(code);}

This validatePostalCode function happens to be using regular expressions (also known as RegExp and regex), delimited by forward slashes, to define a pattern of characters to match a string against. You can read more about these constructs in Chapter 6, Primitive and Built-In Types.

Unfortunately, due to our haste, we didn't question the functionality of the shipping_address_validator package. We assumed it did what it says on the tin. One week after releasing our code to production we get a bug report saying that some users are unable to enter their address information. We look at the code and realize, to our horror, that it only validates US ZIP codes, not all countries' postal codes (for example, it doesn't work on UK postcodes, such as GR82 5JY).

Through this unfortunate series of events, this piece of code is now responsible for blocking the shipment of vital medication to customers all over the world, numbering in the thousands. Fortunately, fixing it doesn't take too long.

Forgetting for a moment who is responsible for this mishap, I'd like to pose the following question: who are the users of this code?

We, the programmers, who decided to use the

shipping_address_validator

package?

The unwitting customers who are attempting to enter their addresses?

The patients in the hospitals who are left waiting for their medication?

There isn't a clear-cut answer to this question. When bugs appear in the code, we can see how there can be massive unfortunate downstream effects. Should the original programmer of the package be concerned with all these downstream dependencies? When a plumber is hired to fix a tap on a sink, should they only consider the function of the tap itself, or the sink into which it pours?

When we write code, we are defining an implicit specification. This specification is communicated by its name, its configuration options, its inputs, and its outputs. Anyone who uses our code has the right to expect it to work according to its specifications, so the more explicit we can be, the better. If we're writing code that only validates US ZIP codes, then we should name it accordingly. When people create software atop our code, we can't have any control over how they use it. But we can communicate explicitly about it, ensuring that its functionality is clear and expected.

It's important to consider all use cases of our code, to imagine how it might be used and what expectations humans will have about it, programmers and end users alike. What we are responsible or accountable for is up for debate, and is as much a legal question as a technical one. But the question of who our users are is entirely up to us. In my experience, the better programmers consider the full gamut of users, aware that the software they write does not exist in a vacuum. 

What is the problem? 

We've spoken about the importance of the user in programming, and how we must first understand what it is they wish to do if we are to have any hope of helping them. 

Only by understanding the problem can we begin to assemble requirements that our code will have to fulfill. In the exploration of the problem, it's useful to ask yourself the following questions:

What problem is the user encountering?

How do they currently carry out this task?

What existing solutions are there and how do they work?

When we have assembled a complete understanding of the problem, we can then begin ideating, planning, and writing code to solve it. At each step, often without realizing it, we will be modeling the problem in a way that makes sense to us. The way we think about the problem will have a drastic effect on the solution we end up creating. The model of the problem we create will dictate the code we end up writing.

What is the model of a problem?A model or conceptual model is a schematic or representation that describes how something works. We create and adapt models all the time without realizing it. Over time, as you gain more information about a problem domain, your model will improve to better match reality.

Let's imagine for a moment that we are responsible for a note-taking application for students and are tasked with creating a solution to the following problem that a user has expressed:

"I have many notes for my studies and so am finding it hard to organize them. Specifically, when trying to find a note on a given topic, I'll try to use the Search feature but I rarely find what I'm looking for since I can't always recall the specific text I wrote."

We've decided that this warrants changes to the software because we've heard similar things from other users. So, we sit down and try to come up with various ideas for how we could improve the organization of notes. There are a few options we could explore:

Categories

: There would be a hierarchical folder structure for categories. A note on

Giraffes

 might exist under studies/zoology. Categories can be easily navigated manually or via search.

Tags

: There would be the ability to

tag

 a note with one or more words or phrases. A note on

Giraffes

 might be tagged with 

mammal

 and

long neck

. Tags can then be easily navigated manually or via search.

Links

: Introduce a

linking

 feature so notes can link to other notes that are related. A note on

Giraffes

 might be linked to from another note, such as the one on

Animals with long necks.

Each solution has its pros and cons, and there is also the possibility of implementing a combination of them. One thing that becomes immediately obvious is that each of these will quite drastically affect how users end up using the application. We can imagine how users exposed to these respective solutions would hold the model of note-taking in their minds:

Categories

: Notes I write have their place in my categorical hierarchy

Tags

: Notes I write are about many different things

Links

: Notes I write are related to other notes I write

In this example, we're developing a UI, so we are sitting very close to the end user of the application. However, the modeling of problems is applicable to all of the work we do. If we were creating a pure REST API for note-keeping, exactly the same considerations would need to be made. Web programmers play a key part in deciding what models other people end up employing. We should not take this responsibility lightly.

Truly understanding the problem domain

The first point of failure is typically misunderstanding the problem. If we don't understand what users are truly trying to accomplish, and we have not received all requirements, then we will inevitably retain a bad model of the problem and thus end up implementing the wrong solutions.

Imagine that this scenario occurs at some point before the invention of the kettle:

Susanne (engineer)

: Matt, we've been asked to design a vessel that users can boil water with

Matthew (engineer)

: Understood; I will create a vessel that does exactly that

Matthew asks no questions and immediately gets to work, excited at the prospect of putting his creativity to use. One day later he comes up with the following contraption:

We can see, quite obviously, that Matthew has forgotten one key component. In his haste, he did not stop to ask Susanne for more information about the user, or about their problem, and so did not consider the eventuality that a user would need to pick up the boiling-hot vessel somehow. After receiving feedback, naturally, he designed and introduced a handle to the kettle:

This needn't have occurred at all, though. Imagine this kettle scenario extrapolated to the complexity and length of a large software project spanning multiple months. Imagine the headaches and needless pain involved in such a misunderstanding. The key to designing a good solution to a problem requires, first and foremost, a correct and complete model of the problem. Without this, we'll fail before we even begin. This matters in the design of massive projects but also in the implementation of the smallest JavaScript utilities and components. In every line of code we write, in fact, we are utterly liable to failure if we do not first understand the problem domain.

The problem domain encapsulates not only the problem being encountered by the user but also the problem of meeting their needs via the technologies we have available. And so, the problem domain of writing JavaScript in the browser, for example, includes the complexity of HTTP, the browser object model, the DOM, CSS, and a litany of other details. A good JavaScript programmer has to be adept not only in these technologies but also in understanding new domains of problems encountered by their users.

Writing code for humans

This entire book is concerned with teaching you how to write clean code in JavaScript. In the following chapters, we'll go into a lot of detail, with discussions of almost every construct within the language. Firstly, we need to establish a few key perspectives that'll be important when we think about what it means to write clean code for humans.

Meaningful abstractions

As we write code, we use and create abstractions constantly. Abstraction is what occurs when we take a piece of complexity and then present access to that complexity in a simpler way. By doing this, we enable people to have leverage over that complexity without having to wield a full understanding of it. This idea underpins most modern technology:

JavaScript, like many other high-level languages, presents an abstraction that enables us not to have to worry about the details of how a computer operates. We can, for example, ignore the problem of memory allocation. Even though we must be sensitive to the constraints of hardware, especially on mobile devices, we'll rarely ever think about it. The language doesn't require us to.

The browser, too, is a famous abstraction. It provides a GUI that abstracts away, among many other things, the details of HTTP communication and HTML rendering. Users can easily browse the internet without ever having to worry about these mechanisms.

In the following chapters of this book, we'll learn more about what it takes to craft a good abstraction. For now, it's enough to say this: in every line of code you write, you are using, creating, and communicating abstractions. 

The tower of abstraction

The tower of abstraction is a way of looking at the complexity of technology. At the base layer, we have the hardware mechanisms depended upon in computation, such as transistors in the CPU and memory cells in RAM. Above that, we have integrated circuits. Above that, you have machine code, assembly, and the operating system. And above that, several layers up, you have the browser, and its JavaScript runtime. Each layer abstracts away complexity so that the layer above can leverage that complexity without too much effort:

When we write JavaScript for the browser, we are already operating on a very tall tower of abstraction. The higher this tower gets, the more precariously it operates. We are dependent on every individual part working as expected. It's a fragile system.

The tower of abstraction is a useful analogy when thinking about our users as well. When we write code, we are adding to this tower, building upon it layer by layer. Our users will always be situated above us on this tower, using the machinery we've crafted to drive their own ends. These users may be other programmers that utilize our code, building yet more layers of abstraction into the system. Alternatively, our users may be the end users of the software, typically sitting atop the tower and leveraging its vast complexity by a simplified GUI.

The layers of clean code

In the next part of the book, we will take the foundational concepts we've talked about in this chapter and build atop them with our own abstractions; these abstractions are the ones we, in the software industry, use to talk about what it means to write clean code.

If we say that our software is reliable or usable, then we are employing abstract concepts. And these concepts must be delved into. We will also be unpicking the innards of JavaScript in later chapters, seeing what it means to deal with the individual pieces of syntax that power our programs. By the end of the book, we should be able to say that we have complete knowledge of multiple layers of clean code, from individually readable lines of code to well-designed and reliable architectures.

Summary

In this chapter, we have built ourselves a great foundation, exploring the very fundamentals that underpin all of the code we write. We have discussed how our code is an expression of intent, and how, in order to build that intent, we must have a sound understanding of what the user requires and the problem domain we are engaging in. We have also explored how we can write code that is clear and readable to humans, and how we can create clear abstractions that provide users with the ability to leverage complexity.

In the next chapter, we will build on this foundation with the specific tenets of clean code: reliability, efficiency, maintainability, and usability. These tenets will be lending us a vital perspective as we go on to study the many facets of JavaScript and how we can wield it in the service of clean code.

The Tenets of Clean Code

In the last chapter, we discussed the purpose that sits at the very beginning of a piece of code: solving a problem for a user. We discussed the difficulties of catering to both the machine and the human. We reminded ourselves that, at its core, writing code is about communicating intent.

In this chapter, we will derive four core tenets from those foundations that are necessary to think about when creating software. These tenets are reliability, efficiency, maintainability, and usability. A good piece of software can be said to have all of these qualities. A bad piece of software can be said to have none of them. Crucially, however, these tenets are not rules. Instead, it is useful to see them as lenses through which you can look at your code. For each tenet, we will discover why it is important through a mix of analogies and JavaScript examples. You should come away from this chapter with an ability to apply these tenets to your code.

Specifically, we will be covering the following tenets:

Reliability

Efficiency

Maintainability

Usability

Reliability

Reliability is the core underpinning of a good software system. Without reliability, the usefulness of technology quickly dissipates, leaving us in a position where we may have been better going without it. Technology's entire purpose can be undermined by unreliability.

Reliability, however, is not only a characteristic of large and complex software systems. Each and every line of code can be constructed to be either unreliable or reliable. But what do we mean by this? Reliability, the word, refers to the quality of being reliable. What does it mean to write code that people can rely on? It helps by defining reliability in terms of three distinct qualities: Reliability is the quality of being correct, stable, and resilient.

Correctness

Code that is correct is code which conforms to a set of expectations and requirements. If I write a function to validate email addresses, then the expectation is that the function can be called with all types of email addresses and correctly establish their validity or invalidity as follows:

isValidEmail('[email protected]'); // => trueisValidEmail('[email protected]'); // => trueisValidEmail('john@thecompany.$$$'); // => falseisValidEmail('this is not an email'); // => false

To write correct code, we must first have an idea of what the requirements are. Requirements are our formalized expectations for how the code will behave. For the previous case of an email validation function, we might have the following requirements:

The function will return

true

when a valid email address is passed as the first argument

The function will otherwise return

false

The first requirement is ambiguous though. We need to discern what it means for an email address to even be valid. Email addresses are a seemingly simple format; however, there are in fact many edge cases and oddly valid manifestations. For example, the following email addresses are technically valid according to the RFC 5322 specification:

admin@mailserver1

[email protected]

[email protected]

To know whether our function should align fully with the RFC specification, we first need to understand its true use case. Is it a utility for email client software, or is it perhaps utilized in user registration for a social media website? In the latter case, we may want to establish the more exotic email addresses as invalid, similar to those listed previously. We may even want to ping the mail server at the domain to confirm its existence. The point is to discern what exact requirements will provide for us the meaning of correct.

Incidentally, composing your own email validation function is very ill-advised, as there are many edge cases that need to be taken into account. This highlights an important consideration in our quest for reliability; often, we can achieve the highest level of reliability by using existing tried-and-tested open source libraries and utilities. In Chapter 17, Other Peoples' Code, we discuss the process of selecting third-party code in detail, and what to look out for.

The requirements we are coding should always derive directly from how our code will be used. It is vital to start with the user and their problem; from that, we can establish a set of clear requirements that can be independently tested. Testing our code is necessary so that we can confirm, to ourselves and our stakeholders, that our code fulfills all of the distinct requirements.

With the previous example of email address validation, a good set of tests would encompass many variations of email addresses, ensuring that all edge cases are fully accounted for. In Chapter 13, The Landscape of Testing, we discuss testing in far more detail. For now, however, it's sufficient to simply reflect on the importance of correctness and the ways in which we can establish and confirm it:

Understand the problem being solved and what users want

Refine your requirements until it's explicitly clear what's required

Test your code to verify its correctness

Stability

Stability is a characteristic that we desire in all technology. Without stability, things get precarious; we become unsure of whether things will break at any moment. Stability is best exemplified by a common piece of real-world technology. Compare these two bridges:

[Photos from Unsplash / by Zach Lezniewicz / by Jacalyn Beales]

They both technically operate correctly as bridges. However, one has previously suffered damage and has been fixed with a simple plank of wood. Which bridge would you trust to safely convey a hundred people across? Probably the one on the right. It is firmly in place with guard rails, and, crucially, no gaps through which you can fall. 

In code, we can say that stability is about the continued correct behavior given different valid inputs and situations. JavaScript in the browser is especially liable to failures in this way. It has to run in a multitude of conditions, on different hardware, operating systems, screen sizes, and often in browsers that have varying capabilities.

If we write code that is strongly dependent on certain conditions, then it may be unwieldy and undependable when those conditions do not exist. If, for example, we were designing and implementing a layout for a web application, we may forget to consider and cater for screen sizes less than 1,024 pixels wide, resulting in the following mess:

This is an example of instability. The web application cannot be depended upon to deliver its correct behavior when a certain environmental factor is different. A situation in which screen size is less than 1,024 pixels is entirely possible and reasonable in a world of increasing mobile device usage; it's an absolutely valid use case of our web application and failure to accommodate it has a negative effect on our user's ability to rely on it.

Stability is gained through having a full understanding of all the different valid inputs and situations that your code may be exposed to. Similar to correctness, stability is best confirmed with a set of tests that expose your code to the full gamut of inputs and situations.

Resilience

Resilience is about avoiding failure. Where stability mostly concerns itself with expected inputs, resilience concerns itself with what happens when your code is exposed to unexpected or nonroutine inputs. Resilience in software systems is also known as fault tolerance and is sometimes spoken about in terms of redundancies or contingencies. Fundamentally, these are all in service of the same goal—to minimize the effects of failure.