Skip to content

Commit 489f3d8

Browse files
committed
Address feedback from @mathias
1 parent 296ffb1 commit 489f3d8

File tree

5 files changed

+88
-55
lines changed

5 files changed

+88
-55
lines changed

chapters/ch01.asciidoc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Embracing module thinking is understanding that complexity is, ultimately, inesc
1919

2020
Systems can be organized granularly: we can split them into projects, made of multiple applications, containing several application-level layers, where we can have hundreds of modules, made up of thousands of functions, and so on. A granular approach helps us write code that's easy to understand and maintain, by attaining a reasonable degree of modularity, while preserving our sanity. In section 1.4, we'll discuss how we can best leverage this granularity to create modular applications.
2121

22-
Whenever we delineate a component, there's going to be a public interface other parts of the system can leverage to access our component. The interface -- or API -- will be comprised of the set of methods or attributes that our component exposes. These methods or attributes, can also be referred to as touchpoints, that is, the aspects of the interface that can be publicly interacted with. The fewer touchpoints an interface has, the smaller its surface area is, and the simpler the interface becomes. An interface with a large surface area is highly flexible, but it might also be a lot harder to understand and use, given the high amount of functionality exposed by the interface.
22+
Whenever we delineate a component, there's going to be a public interface which other parts of the system can leverage to access our component. The interface -- or API -- is comprised of the set of methods or attributes that our component exposes. These methods or attributes, can also be referred to as touchpoints, that is, the aspects of the interface that can be publicly interacted with. The fewer touchpoints an interface has, the smaller its surface area is, and the simpler the interface becomes. An interface with a large surface area is highly flexible, but it might also be a lot harder to understand and use, given the high amount of functionality exposed by the interface.
2323

2424
This interface serves a dual purpose. It allows us to develop new bits and pieces of the component, only exposing functionality that's ready for public consumption while keeping private everything that's not meant to be shared with other components. At the same time, it allows consumers -- that is, components or systems that are leveraging our interface -- to reap the benefits of the functionality we expose, without concerning themselves with the details of how we implemented that functionality.
2525

@@ -154,7 +154,7 @@ Much like RequireJS and AngularJS, CommonJS dependencies are also referred to by
154154

155155
Unlike RequireJS or AngularJS, CommonJS was rather strict. In RequireJS and AngularJS you could have many dynamically-defined modules per file, whereas CommonJS had a one-to-one mapping between files and modules. At the same time, RequireJS had several ways of declaring a module and AngularJS had several kinds of factories, services, providers and so on -- besides the fact that its dependency injection mechanism was tightly coupled to the AngularJS framework itself. CommonJS, in contrast, had a single way of declaring modules. Any JavaScript file was a module, calling `require` would load dependencies, and anything assigned to `module.exports` was its interface. This enabled better tooling and code introspection -- making it easier for tools to learn the hierarchy of a CommonJS component system.
156156

157-
Eventually, Browserify was invented as a way of bridging the gap between CommonJS modules for Node.js servers and the browser. Using the `browserify` command-line interface program and providing it with the path to an entry point module, one could combine an unthinkable amount of modules into a single browser-ready bundle. The killer feature of CommonJS, the npm package registry, was decisive in aiding its takeover of the module loading ecosystem.
157+
Eventually, Browserify was invented as a way of bridging the gap between CommonJS modules for Node.js servers and the browser. Using the `browserify` command-line interface program and providing it with the path to an entry-point module, one could combine an unthinkable amount of modules into a single browser-ready bundle. The killer feature of CommonJS, the npm package registry, was decisive in aiding its takeover of the module loading ecosystem.
158158

159159
Granted, npm wasn't limited to CommonJS modules or even JavaScript packages, but that was and still is by and large its primary use case. The prospect of having thousands of packages (now over half a million and steadily growing) available in your web application at the press of a few fingertips, combined with the ability to reuse large portions of a system on both the Node.js web server and each client's web browser, was too much of a competitive advantage for the other systems to keep up.
160160

@@ -174,15 +174,15 @@ import('./mathlib').then(mathlib => {
174174

175175
In ESM, too, every file is a module with its own scope and context. One major advantage in ESM over CJS is how ESM has -- and encourages -- a way of statically importing dependencies. Static imports vastly improve the introspection capabilities of module systems, given they can be analyzed statically and lexically extracted from the abstract syntax tree (AST) of each module in the system. Static imports in ESM are constrained to the topmost level of a module, further simplifying parsing and introspection.
176176

177-
In Node.js v8.5.0, ESM module support was introduced behind a flag. Most evergreen browsers also support ESM modules behind flags.
177+
In Node.js v8.5.0, ESM support was introduced behind an `--experimental-modules` flag -- provided that we use the `.mjs` file extension for our modules. Most evergreen browsers already support ESM without flags.
178178

179-
Webpack is a successor to Browserify that largely took over in the role of universal module bundler thanks to a broader set of features. Just like in the case of Babel and ES6, Webpack has long supported ESM with both its `import` and `export` statements as well as the dynamic `import()` function. It has made a particularly fruitful adoption of ESM, in no little parts thanks to the introduction of a "code-splitting" mechanism whereby it's able to partition an application into different bundles to improve performance on first load experiences.
179+
Webpack is a successor to Browserify that largely took over in the role of universal module bundler thanks to a broader set of features. Just like in the case of Babel and ES6, Webpack has long supported ESM with both its static `import` and `export` statements as well as the dynamic `import()` function-like expression. It has made a particularly fruitful adoption of ESM, in no little parts thanks to the introduction of a "code-splitting" mechanism whereby it's able to partition an application into different bundles to improve performance on first load experiences.
180180

181181
Given how ESM is native to the language, -- as opposed to CJS -- it can be expected to completely overtake the module ecosystem in a few years time.
182182

183183
=== 1.3 The Perks of Modular Design
184184

185-
We've already addressed the fact that modularity, as opposed to a single shared global scope, helps avoid unexpected clashes in variable names thanks to the diversification of scoping across modules. Beyond a fix for clashes, modularity spread across files limits the amount of complexity we have to pay attention to when working on any one particular feature. In doing so, our team will be able to focus on the task at hand and be more productive as a result.
185+
We've already addressed the fact that modularity, as opposed to a single shared global scope, helps avoid unexpected clashes in variable names thanks to the diversification of scoping across modules. Beyond a fix for clashes, modularity spread across files limits the amount of complexity we have to pay attention to when working on any one particular feature. In doing so, our team is able to focus on the task at hand and be more productive as a result.
186186

187187
Maintainability, or the ability to effect change in the codebase, also improves significantly because of this. When code is simple and modular, it's easier to build upon and extend. Maintainability is valuable regardless of team size: even in a team of one, if we leave a piece of code untouched for a few months and then come back to it, it might be hard to improve upon or even understand if we didn't consider writing maintainable code the first time around.
188188

@@ -212,7 +212,7 @@ Going back to performance, we should be treating it as a feature, where for the
212212

213213
We, as developers, often over-do architecture as well, and a lot of the reasoning about performance optimization applies here as well. Laying out an all-encompassing architecture that has the potential to save us trouble as we scale to billions of transactions per second might cost us considerable time spent upfront and possibly also lock us into a series of abstractions that will be hard to keep up with, for no foreseeable gains in the near term. It's a lot better when we focus on problems we're already running into, or might soon run into, instead of trying to plan for a hockey-stick growth of infrastructure and throughput without any data to back up the hockey-stick growth we're anticipating.
214214

215-
When we don't plan in such a long-term form, an interesting thing occurs, our systems will grow more naturally, adapting to the needs of the near-term, gradually progressing towards support for a larger application and larger set of requirements. When that progression is gradual, we'll notice a corrective behavior in how abstractions are picked up or discarded as we grow. If we settle on abstractions too early, and they end up being the wrong abstractions, we'll pay dearly for that mistake. Bad abstractions force us to bend entire applications to their will, and once we've realized that the abstraction is bad and ought to be removed, we might be so heavily invested in it that pulling out might be costly. This, paired with the sunk cost fallacy, whereby we're tempted to keep the abstraction just because we've spent a lot of time, sweat, and blood on it, can be very hazardous indeed.
215+
When we don't plan in such a long-term form, an interesting thing occurs: our systems grow more naturally, adapting to the needs of the near-term, gradually progressing towards support for a larger application and larger set of requirements. When that progression is gradual, we notice a corrective behavior in how abstractions are picked up or discarded as we grow. If we settle on abstractions too early, and they end up being the wrong abstractions, we pay dearly for that mistake. Bad abstractions force us to bend entire applications to their will, and once we've realized that the abstraction is bad and ought to be removed, we might be so heavily invested in it that pulling out might be costly. This, paired with the sunk cost fallacy, whereby we're tempted to keep the abstraction just because we've spent a lot of time, sweat, and blood on it, can be very hazardous indeed.
216216

217217
We'll devote an important part of this book to understanding how we can identify and leverage the right abstractions at the right time, so that the risk they incur is minimized.
218218

@@ -222,7 +222,7 @@ Due to its history, JavaScript is particularly interesting when it comes to modu
222222

223223
The lack of native modularity in the web -- due to the way a program is loaded in chunks using HTML `<script>` tags -- is in stark contrast with any other execution environments where programs can be made up of any number of files and modular architectures are natively supported by the language, its compiler, and its file system based environment. On the web, we're only now barely beginning to scratch the surface of native modules, something other programming environments have had since their inception. As we discussed in section 1.2, the lack of a native module loading mechanism, paired with the lack of native modules beyond just files that shared a global scope, forced the web community to get creative in its approach to modularity.
224224

225-
The native JavaScript modules specification that eventually landed into the language was heavily influenced by this community-led effort. Even as of this writing we're still probably some 5 years away from being able to use the native module system effectively on the web. This shortcoming of the web is evidenced by how patterns that were adopted universally elsewhere, like layered or component-based architectures, weren't even contemplated on the web for most of its lifetime thus far.
225+
The native JavaScript modules specification that eventually landed into the language was heavily influenced by this community-led effort. Even as of this writing we're still probably some 2 or 3 years away from being able to use the native module system effectively on the web. This shortcoming of the web is evidenced by how patterns that were adopted universally elsewhere, like layered or component-based architectures, weren't even contemplated on the web for most of its lifetime thus far.
226226

227227
Up until the launch of a Gmail beta client in April, 2004, which demonstrated the power of asynchronous JavaScript HTTP requests to provide a single-page application experience, and then the initial release of jQuery in 2006, which provided a hassle-free cross-browser web development experience, JavaScript was seldom regarded as a serious modern development platform.
228228

0 commit comments

Comments
 (0)