Skip to content

Commit c4786ce

Browse files
committed
review changes
1 parent 5e070e7 commit c4786ce

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

chapters/ch02.asciidoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
[[modularity-principles]]
22
== Modularity Principles
33

4+
Modularity can be the answer to complexity, but what exactly do we mean when we're talking about complexity?
5+
46
Complexity is a loaded term for a nuanced topic. What does complex mean? A dictionary defines complexfootnoteref:[define-complex,The dictionary definition might help shed a light on this topic: https://mjavascript.com/out/complex.] as something that's "composed of many interconnected parts", but that's not the problem we generally refer to when we speak of complexity in the context of programming: a program may have hundreds or thousands of files and still be considered relatively simple.
57

68
The next two definitions, offered by that same dictionary, might be more revealing in the context of program design.
@@ -156,6 +158,8 @@ export default function send (options, done) {
156158
}
157159
----
158160

161+
We've been discussing API design in terms of responsibility, but something equally interesting is that we've hardly worried about the implementation of those interfaces. Is there merit to designing an interface before digging into its implementation?
162+
159163
==== 2.1.2 API First
160164

161165
A module is only as good as its public interface. A poor implementation may hide behind an excellent interface. More importantly, a great interface means we can swap out a poor implementation as soon as we find time to introduce a better one. Since the API remains the same, we can decide whether to replace the existing implementation altogether or if both should co-exist while we upgrade consumers to use the newer one.
@@ -474,7 +478,7 @@ In another example, we could implement a Markdown compiler function with a defau
474478

475479
Going back to `fetch`, note how little configuration or implementation-specific knowledge we need for the simplest case. This hardly changes when we need to choose the HTTP method, since we just need to add an option. Well designed interfaces have a habit of making it appear effortless for consumers to use the API for its simplest use case, and have them spend a little more effort for slightly more complicated use cases. As the use case becomes more complicated, so does the way in which the interface needs to be bent. This is because we're taking the interface to the limit, but it goes to show how much work can be put into keeping an interface simple by optimizing for common use cases.
476480

477-
==== 2.2.5 Smallest possible surface area
481+
==== 2.2.5 Tiny surface areas
478482

479483
Any interface benefits from being its smallest possible self. A small surface area means fewer test cases that could fail, fewer bugs that may arise, fewer ways in which consumers might abuse the interface, less documentation, and more ease of use since there's less to choose from.
480484

@@ -483,3 +487,5 @@ The malleability of an interface depends on the way it is consumed. Functions an
483487
Not all changes are breaking changes, however. We might learn from an interface like the one in `fetch`, for example, which remains highly malleable even in the face of change. Even though the interface is tiny for it's simplest use case, -- `GET /resource` -- the `options` parameter can grow by leaps and bounds without causing trouble for consumers, while extending the capabilities of `fetch`.
484488

485489
We can avoid creating interfaces that contain several slightly different solutions for similar problems by holistically designing the interface to solve the underlying common denominator, maximizing the reusability of a component's internals in the process.
490+
491+
Having established a few fundamentals of module thinking and interface design principles, it's time for us to shift our attention to module internals and implementation concerns.

chapters/ch03.asciidoc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
[[module-design]]
22
== Module Design
33

4-
Having established a few fundamentals of module thinking and interface design principles, it's time for us to shift our attention to module internals and implementation concerns. Thinking in terms of API-driven and documentation-driven design will yield more usable modules than not doing so. You might argue that internals are not that important: "as long as the interface holds, we can put anything we want in the mix!". A usable interface is only one side of the equation, but it will do little to keep the maintainability of your applications in check. Properly designed module internals help keep our code readable and its intent clear.
4+
Thinking in terms of API-driven and documentation-driven design will yield more usable modules than not doing so. You might argue that internals are not that important: "as long as the interface holds, we can put anything we want in the mix!". A usable interface is only one side of the equation, but it will do little to keep the maintainability of your applications in check. Properly designed module internals help keep our code readable and its intent clear. In this chapter we'll debate what it takes to write modules with scalability in mind, but without getting too far ahead of our current requirements. We'll discuss the CRUST constraints in some more depth, and finally ellaborate on how to prune modules as they become larger and more complex over time.
55

66
=== 3.1 Growing a Module
77

88
Small, single-purpose functions are the lifeblood of clean module design. Purpose-built functions scale well because they introduce little organizational complexity into the module they belong to, even when that module grows to 500 lines of code. Small functions are not necessarily less powerful than large functions, but their power lies in composition.
99

1010
Suppose that instead of implementing a single function with 100 lines of code we break it up into 3 or more smaller functions. We might later be able to reuse one of those smaller functions somewhere else in our module, or it might prove a useful addition to its public interface.
1111

12-
In this chapter we'll discuss design considerations aimed at reducing complexity at the module level. While most of the concerns we'll discuss here have an effect on the way we write functions, it is in the next chapter where we'll be devoting our time to the art of writing less complex functions.
12+
In this chapter we'll discuss design considerations aimed at reducing complexity at the module level. While most of the concerns we'll discuss here have an effect on the way we write functions, it is in the next chapter where we'll be specifically devoting our time to the development of simple functions.
1313

1414
==== 3.1.1 Composability and Scalability
1515

@@ -203,6 +203,6 @@ However convincing an eloquent piece of advice or tool might seem, always apply
203203

204204
Whenever you're analyzing whether a dependency, tool, or piece of advice fits your needs, always start by reading what there is to be read and consider whether the problem being solved is one you indeed need to solve. Avoid falling in the trap of leveraging advice or tools merely because it became popular or is being hailed by a large actor.
205205

206-
Never overcommit to that which you're not certain fits your needs, but always experiment. It is by keeping an open mind that we can capture new knowledge, improve our understanding of the world, and innovate. This is aided by critical thinking and hindered by rushing to the newest technology without firsthand experimentation.
206+
Never overcommit to that which you're not certain fits your needs, but always experiment. It is by keeping an open mind that we can capture new knowledge, improve our understanding of the world, and innovate. This is aided by critical thinking and hindered by rushing to the newest technology without firsthand experimentation. In any case, rules are meant to be bent, and broken.
207207

208-
In any case, rules are meant to be bent, and broken.
208+
Let's move into the next chapter, where we'll decypher the art of writing less complex functions.

chapters/ch04.asciidoc

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -507,13 +507,22 @@ At its heart, state is mutable. Even if the variable bindings themselves are imm
507507

508508
Consider a game of chess, where each of two players starts with 16 pieces, each deterministically assigned a position on a checkerboard. The initial state is always the same. As each player inputs their actions, moving and trading pieces, the system state mutates. A few moves into the game, there is a good chance we'll be facing a game state we haven't ever experienced before. Computer program state is a lot like a game of chess, except there's more nuance in the way of user input, and an infinitude of possible board positions and state permutations.
509509

510-
In the world of web development, a human decides to open a new tab in their favorite web browser and they then google for "cat in a pickle gifs". The browser allocates a new process through a system call to the operating system, which shifts some bits around on the physical hardware that lies inside the human's computer. Before the HTTP request hits the network, we need to hit DNS servers, engaging in the elaborate process of casting `google.com` into an IP address. The browser then checks whether there's a ServiceWorker installed, and assuming there isn't one the request finally takes the default route of querying Google's servers for "cat in a pickle gifs". Naturally, Google receives this request at one of the front-end edges of its public network, in charge of balancing the load and routing requests to healthy back-end services. The query goes through a variety of analyzers that attempt to break it down to its semantic roots, stripping the query down to its essential keywords in an attempt to better match relevant results. As the search engine figures out the 10 most relevant results for "cat pickle gif" out of billions of pages in its index -- which was of course primed by a different system that's also part of the whole -- Google pulls down a highly targeted piece of relevant advertisement about cat gifs that matches what they believe is the demographic the human making the query belongs to, thanks to a sophisticated ad network, figures out whether the user is authenticated with Google through an HTTP header session cookie and the search results page starts being constructed and streamed to the human, who now appears impatient and fidgety. As the first bits of HTML being streaming down the wire, the search engine produces its results and hands them back to the front-end servers, which includes it in the HTML stream that's sent back to the human. The web browser has been working hard at this too, parsing the incomplete pieces of HTML that have been streaming down the wire as best it could, even daring to launch other admirably and equally-mind-boggling requests for HTTP resources presumed to be JavaScript, CSS, font, and image files as the HTML continues to stream down the wire. As the first few chunks of HTML are converted into a DOM tree, the browser would finally be able to begin rendering bits and pieces of the page on the screen, if it weren't because it's still waiting on those equally-mind-boggling CSS and font requests. As the CSS stylesheets and fonts are transmitted, the browser begins modeling the CSSOM and getting a more complete picture of how to turn the HTML and CSS plain text chunks provided by Google servers into a graphical representation that the human finds pleasant. Browser extensions get a chance to meddle with the content, removing the highly targeted piece of relevant advertisement about cat gifs before I even realize Google hoped I wouldn't block ads this time around. A few seconds have passed by since I first decided to search for "cat in a pickle gifs". Needless to say, thousands of others brought similarly inane requests to the same systems during this time.
510+
In the world of web development, a human decides to open a new tab in their favorite web browser and they then google for "cat in a pickle gifs". The browser allocates a new process through a system call to the operating system, which shifts some bits around on the physical hardware that lies inside the human’s computer. Before the HTTP request hits the network, we need to hit DNS servers, engaging in the elaborate process of casting `google.com` into an IP address. The browser then checks whether there’s a ServiceWorker installed, and assuming there isn’t one the request finally takes the default route of querying Google’s servers for the phrase “cat in a pickle gifs”.
511511

512-
Not only does this example demonstrate the marvelous machinery and infrastructure that fuels even our most flippant daily computing experiences, but it also illustrates how abundantly hopeless it is to make sense of a system as a whole, let alone its comprehensive state at any given point in time. After all, where do we draw the boundaries? Within the code we wrote? The code that powers our customer's computers? Their hardware? The code that powers our servers? Its hardware? The internet as a whole? The power grid?
512+
Naturally, Google receives this request at one of the front-end edges of its public network, in charge of balancing the load and routing requests to healthy back-end services. The query goes through a variety of analyzers that attempt to break it down to its semantic roots, stripping the query down to its essential keywords in an attempt to better match relevant results.
513+
514+
The search engine figures out the 10 most relevant results for “cat pickle gif” out of billions of pages in its index – which was of course primed by a different system that’s also part of the whole – and at the same time, Google pulls down a highly targeted piece of relevant advertisement about cat gifs that matches what they believe is the demographic the human making the query belongs to, thanks to a sophisticated ad network, figures out whether the user is authenticated with Google through an HTTP header session cookie and the search results page starts being constructed and streamed to the human, who now appears impatient and fidgety.
515+
As the first few bits of HTML being streaming down the wire, the search engine produces its results and hands them back to the front-end servers, which includes it in the HTML stream that’s sent back to the human. The web browser has been working hard at this too, parsing the incomplete pieces of HTML that have been streaming down the wire as best it could, even daring to launch other admirably and equally-mind-boggling requests for HTTP resources presumed to be JavaScript, CSS, font, and image files as the HTML continues to stream down the wire. The first few chunks of HTML are converted into a DOM tree, and the browser would finally be able to begin rendering bits and pieces of the page on the screen, if it weren’t because it’s still waiting on those equally-mind-boggling CSS and font requests.
516+
517+
As the CSS stylesheets and fonts are transmitted, the browser begins modeling the CSSOM and getting a more complete picture of how to turn the HTML and CSS plain text chunks provided by Google servers into a graphical representation that the human finds pleasant. Browser extensions get a chance to meddle with the content, removing the highly targeted piece of relevant advertisement about cat gifs before I even realize Google hoped I wouldn’t block ads this time around.
518+
519+
A few seconds have passed by since I first decided to search for cat in a pickle gifs. Needless to say, thousands of others brought similarly inane requests. To the same systems. During this time.
520+
521+
Not only does this example demonstrate the marvelous machinery and infrastructure that fuels even our most flippant daily computing experiences, but it also illustrates how abundantly hopeless it is to make sense of a system as a whole, let alone its comprehensive state at any given point in time. After all, where do we draw the boundaries? Within the code we wrote? The code that powers our customer’s computers? Their hardware? The code that powers our servers? Its hardware? The internet as a whole? The power grid?
513522

514523
==== 4.3.2 Eliminating Incidental State
515524

516-
We've established that the overall state of a system has little to do with our ability to comprehend parts of that same system. Our focus in reducing entropy must then lie in the individual aspects of the system. It's for this reason that breaking apart large pieces of code is so effective. We're reducing the amount of state local to each given aspect of the system, and that's the kind of state that's worth taking care of, since it's what we can keep in our heads and make sense of.
525+
We've established that the overall state of a system has little to do with our ability to comprehend parts of that same system. Our focus in reducing state-based entropy must then lie in the individual aspects of the system. Its for this reason that breaking apart large pieces of code is so effective. Were reducing the amount of state local to each given aspect of the system, and thats the kind of state thats worth taking care of, since its what we can keep in our heads and make sense of.
517526

518527
Whenever there's persistance involved, there's going to be a discrepancy between ephemeral state and realized state. In the case of a web application, we could define ephemeral state as any user input that hasn't resulted in state being persisted yet, as might be the case of an unsaved user preference that might be lost unless persisted. We can say realized state is the state that has been persisted, and that different programs might have different strategies on how to convert ephemeral state into realized state. A web application might adopt an Offline-First pattern where ephemeral state is automatically synchronized to an IndexedDB database in the browser, and eventually realized by updating the state persisted on a back-end system. When the Offline-First page is reloaded, unrealized state may be pushed to the back-end or discarded.
519528

0 commit comments

Comments
 (0)