Global Mutable State
dev.to
dev.to
I've worked in mostly backend software for a decade, and web dev is easily the hardest thing I've ever had to work on.
CSS especially is a nightmare. It's near impossible in complex projects to work out if your change is going to change other things adversely. Not only do you have to know all usages of the CSS class (for example), but if it's a scoped class (.foo .bar) you have to know all instances in which is it scoped in this exact way. Because it's visual you're basically testing with your eyes, there is no "refactor to green" possible.
In Javascript it seems normal to (at least in angular) push to global scope, and so it can be really unclear where values come from, or how they get set.
I once tried to work out why a fancy ajaxed select box (rendered in JS / CSS, naturally) was firing its onblur event at the wrong time. There were at least three JS event handlers (from memory, jquery, angular and native) that could possibly have events against it. Everything is smushed together, and because it's about losing or gaining focus the observer effect comes into play, and… it's a nightmare. From memory I just gave up, and I presume that particular bug still exists today.
Testing any of this stuff is also pretty nightmarish. Knowing the web I'm almost certainly behind the curve and there is new hotness out to solve this, but I've never much luck with automated browser testing. Things like Protractor tests are incredibly onerous to write, and inevitably end up as flakey slow messes that fail builds due to flake more often than they find issues.
I imagine there are lots of ways to solve these sorts of things, but all I've read feel like variants and retellings of "do it right", which isn't really that helpful, because a) what is right?, and b) it's easy to slowly slip away from "right" without noticing.
I agree that the culture by default was to program everything against a giant mutable state bucket and I'm not sure why that happened.
IMHO, article does not explain in what cases it can be better to have a global mutable state (i.e. containing mutation in Redux) instead of lots of local mutable state.
You cannot avoid keeping state and mutating it, the point is to do so in a predictable and controlled way. It is not that relevant whether you refer to `state.value` or just `value` as long as you're able to keep track of all your mutation sources easily. Having a global object is just a way of conveniently do that in javascript.
* View Source of anything
* development tools that keep improving by the day
* non-proprietary, open standards
* completely sandboxed from host environment
* runs anywhere
* first-class GUI support
The list goes on...
I really, really have to wonder about "first-class GUI support". Prior to the web applications used standard UI controls. Because that makes sense and makes computers easy to use. The web brought 'every single UI is different', which is horrendous. And when you can manage to put together a UI with web tech, then it's trapped in the browser. Which actively goes out of its way to make it as difficult as possible to manage running several different things at once.
I think the answer to the webs success boils down to delivering what users actually want. Despite all the other warts, its valuable enough to convince them to not care. They want to search for something they want, vaguely, and be using an application one second later. All other considerations be damned. They'll wrestle with confusing UIs that change all the time without warning. They'll accept not having a lick of control over their data. They'll deal with being aggressively advertised to and companies trying to hound them into subscriptions because the 'service' of working around the sandboxing by keeping their data together needs ongoing cash infusions when the user only wanted a single product they could use for long periods of time. Like if people who made hammers started making ones that would only work with nails gotten from the hammer manufacturer and they didn't permit you to get enough to last, requiring a subscription so they could sell the information about what kind of nails and how often you were using them to a nail company.
I don't disagree with most of your post but I am confused why you brought this up as a point of contention. Viewing source is pretty common?
It's possible that I've misunderstood some nuances about the claim, and I apologize if I've misrepresented the comment you're replying to.
And for that matter I learned a heap inspecting (whilst attempting to reverse) Google's culture project. Specifically how they protected their image assets using a novel technique (since this was in pre-web-DRM world).
The other thing is as sites know their code is in the open I suspect it's easier for devs to get internal approval to do write ups of particular techniques.
I tweaked Discord's scrollback limit a few times by hunting down the appropriate constant, setting a breakpoint where it was used, and modifying the value then, to make it work better on my portrait 4k monitors. Couldn't be bothered to proxy the javascript (or otherwise do something persistent to modify the JS) to make the change stick though. Something I did make stick was the removal of "Blocked Message" notifications which I use to filter out particularly noisy bots in poorly moderated channels. That's certainly been within the past few months.
I've got javascriptlets which, while not dependent on the actual source of a website, will speed up HTML5 video to 2/3/4x speed. Past year or so?
I wrote a browser extension that abused the Trello DOM for a bunch of minor UI things and added some nifty time tracking stuff. A few years ago now, admittedly.
Oh sure, the minified sources are terrible to parse through these days, but it's still an option. Meanwhile, I still can't deal with x86 disassembly fluidly enough to reasonably modify other people's programs, despite staring at such disassembly for longer...
(Though, you're absolutely right about CSS. It works fine for global state. The only problem is that "components", i.e. small bits of UI, aren't global.)
Not to mention the usual arguments for CSS just don't make sense. CSS isn't decoupled, in fact the reliance on HTML classes and ids is extremely coupled. JSS can just be left in a separate file and imported wherever it needs to be used.
Without the insight of having ever kept a back-end applications various functions isolated and grouped logically they always just lump everything together.
Define a grid system to handle rough layout, then hang your widgets inside each cell, if you have to use !important, ever, you have failed.
This is solved with shadow DOM.
> In Javascript it seems normal to (at least in angular) push to global scope, and so it can be really unclear where values come from, or how they get set.
Well, nobody forces you to do it in JavaScript. I think newer frameworks (and culture around them) discourages this.
> Testing any of this stuff is also pretty nightmarish. Knowing the web I'm almost certainly behind the curve and there is new hotness out to solve this, but I've never much luck with automated browser testing. Things like Protractor tests are incredibly onerous to write, and inevitably end up as flakey slow messes that fail builds due to flake more often than they find issues.
I agree that testing the whole application is tricky, but testing components is easy enough these days.
Had the web been designed as a distributed application platform with HTML being a user interface layout language and such, things would have developed very differently. And because making it do anything interactive most often required misuse and abuse of the browser, it wasn't much pursued by bona fide software engineers who knew the pitfalls of ad hoc 'organic' development and 'just keep typing until something works' mindsets. So, that's what we got. Big ball of global mutable state, scripting languages designed to make it so you could have a button light up when the mouse hovered over it bent over and made to do real work, dependency webs so complex that they're generating novel unsolved problems for graph theorists at every turn, etc.
All of this because users want to be able to search vaguely for what they want and be using an application a moment later. Something that could be solved a trillion different ways but nobody realized at the time that it is what people really wanted. So now we oh-so-slowly move towards the web becoming an actual application platform, but hamstrung by companies that feel entitled to every data point we ever generate so they seek to insert latency into even the simplest interactions by requiring asking their machines for permission.
Just know that things are fantastically much better than 10 years ago!
Actually, as you guessed, lots of people in the modern web community are unhappy with the mess of mutable state, and have done something about it! Redux [1] is all about using immutable state to tame the mess into a simple model involving application state expressed as an immutable object, the ui produces events, which get piped through a reducer, yielding a new state. If you skim over the "redux motivation" link below you'll see it talks all about the issues you're raising.
There are tons of variants of this theme, including NGRX which is the same concept applied to Angular 2. [2]
> CSS especially is a nightmare. It's near impossible in complex projects to work out if your change is going to change other things adversely...
I feel your pain, and have struggled with this endlessly in the past. However, this is a mostly a well-solved problem in modern environments. For example in Angular 2, the css is automatically scoped for you down to the component level in a sane way[3].
Try React with Redux, or Angular2 with NGRX and see if it solves the issues you're concerned with.
It's enough different options to make your head spin, I know. Just try out Angular 2 or Redux and see what you think.
[1] http://redux.js.org/docs/introduction/Motivation.html [2] http://onehungrymind.com/build-better-angular-2-application-... [3] http://blog.ng-book.com/css-isolation-in-angular-2-component...
Why is it surprising that people are working within the constraints of what is practical right now? What are you going to use instead that is so much better? If you want to do web development, you have to use CSS and JavaScript in some form. Just with backend work, there are good and bad ways of doing frontend coding.
On the JS side, we have projects like Redux that effectively forbid direct mutation of global state by a pattern of describing state mutations using data (Actions), and handling those mutation descriptions in pure functions that take existing state, along with the aforementioned state change descriptions, and return the expected next state without any observable side effects (Reducers). This approach leads to a codebase that is simple to test and to reason about, and enables certain development and debugging workflows that would not be possible otherwise, such as component-level, state-preserving hot-reloading, server-side state hydration and pre-rendering, and time-travelling debugging (See Redux Devtools). The popularity of Redux in production React apps today is a testament to how well this approach scales in terms of app complexity and team size.
In terms of CSS, definitely take a look at styled-components and Glamorous. They are AFAIK the state of the art in component-oriented CSS libraries, and can do anything you can in standard CSS (media-queries, pseudo-selectors, etc), unlike earlier CSS in JS libraries that come with all sorts of tradeoffs and limitations. The major win you get with these libraries is that styles are locally scoped by default and do not extend past component boundaries, so you can ensure each component is solely responsible for styling everything within their own borders, and will be styled consistently regardless of the surrounding context. The global-by-default, specificity-based-overriding, and recklessly-cascading aspects of CSS have long been considered harmful in component oriented architectures (rightfully so, in my experience), and tools like these allow you to opt-out of those properties by default, while still allowing you the opportunity to apply them mindfully and sparingly only when they happen to be the right tools for the job. Also, with Glamorous, which uses JS objects to represent CSS rules, you can programmatically manipulate your styles with everything in JavaScript available at your disposal (no more struggling with crappy DSLs like SCSS and LESS), and can make use of JS variables and modules to enable style reuse and take advantage of Webpack tree shaking to ensure only used CSS rules are included in your final bundle.
Combined with a lightweight vdom library like Mithril.js using the HyperScript API and maintaining state in plain old JavaScript objects (updating on any user interaction, network events, timers, or some few other events), this makes web single-page application development quite pleasant and scalable. You can also test much of such vdom-based UIs without creating real DOM nodes. https://mithril.js.org/
Here is one example FOSS project I wrote in that style: https://github.com/pdfernhout/Twirlip7
Of course, few web developers grok this yet because most affirm "best practices" from years ago (e.g. semantic CSS, HTML-first design, JavaScript as a progressive afterthought) -- and also are used to coding in non-standard HTML-ish templating systems (e.g. Angular, Vue, JSX) which give them a false sense of security they can maintain more complex apps that are mostly about JavaScript. React has helped a lot of developers begin to move past some of that, but it has its own licensing issues and also baggage (including JSX) from being only half-way to the new paradigm outlined above.
Global mutable state is akin to an external interface. The user is a global mutable state. If there is no user, then the external data the program is interacting with is often global mutable state (it may change in arbitrary ways, and your program should behave differently), a database is global mutable state, as is the DOM / UI.
So, beyond the 101-level advice (don't use global variables, prefer explicit state, reduce visibility, etc.), global mutable state is unavoidable, and it needs to be treated as 'external' by any code that uses it. I think that is the problem. Not so much global mutable state, but global mutable state with privileges, global mutable state that any bit of code makes assumptions about.
Program defensively against global mutable state. Reduce it, but don't be religious about it. It's still a tool in the toolbox if needed.
It is basically a language which trains you to try to err on the side of caution.
It's often preferable to be religious when you can be, as even if you do there will still be plenty of bugbears left to watch out for.
I think of the code that I wrote when I first started programming. Nearly every variable was global, because I simply couldn't be bothered to think about where it could be accessed from. But nowadays I can't remember the last time that I used a global variable, and I'm no worse off for it. For beginners it's a tempting tool to turn to, and we need to continue teaching aversion to it. Like `goto`, we can both consider it harmful and also happily allow its use when well-considered.
(Historical fact: very ancient versions of Rust didn't have global state at all, as Graydon was hoping to find a way to avoid ever needing to use it; eventually he was persuaded otherwise. Though global mutable state in Rust still requires the `unsafe` keyword to access due to the risk of concurrent access, so it's rare to see it in the wild (and quite obvious when it is).)
For example, in back-end Java dev, I never use singleton objects in the truest sense of the pattern; singletons cannot be mocked out to test units in isolation. Instead, instantiate it with a dependency injection container and let your framework (Guice, Spring) wire things together. In effect, your global state becomes contained within a single context. Nothing stopping you having two contexts in the same runtime environment though.
I find static methods are okay if they are concise and have no state; in particular if I'd never want to test the caller without it making the call to the static method anyway. This is becoming more important with Java 8's lambdas and needing to write helper methods.
On the other hand, the cost of doing this might not be worthwhile for a game dev? Do game devs write unit tests? I have no idea.
But here's the problem I have. If we don't want people to use global variables, the advice should be 'don't use global variables'. There is no need to wrap it up in 'avoid global mutable state', when using global mutable state is unavoidable in many cases and the best engineering solution in many others.
At the point you have to nuance it to 'reduce your code's exposure use global mutable state, accessing it through mockable interfaces, and defensively checking its values' to mean 'don't use global variables', it may be simpler to say what we mean.
For example: there is a comment lower down in this thread of someone confused by the advice in the context of the game engine. I've worked with hundreds of game engines, every one of them has global mutable state.
Regarding caching your processor tends to feel otherwise and has multiple levels of cache because globally mutating state in a coherent way is slow as molasses.
I can't wait to hear what awesome program needs a connection pool and access to the JIT at the same time. Are you writing a SQL compiler that sends mail?
Is your awesome program by any chance written in JavaScript or PHP?
PS. Databases aren't really global mutable state as in when you ask your database for a count of rows in a table the answer you get back won't ever change because magic.
Also, because of the ways database ARE global mutable state you run into all the issues you typically do with global mutable state. And then the places you do have global mutable state turn into performance nightmares. (Read: Hot spots)
The way I think about it is that if you create a global variable for a user, you are saying that there is always a one-to-one correspondence between a user (maybe the current user) and a process.
In some apps (such as command-line apps) you can assume that the OS user is always the current user. But if you ever write an app that might be used by more than one person, you probably want to keep track of which user you're currently talking about. For example, are there multiple profiles? User switching? Simulated users in tests? There are relatively few objects that we can sure will always be one-to-one with a process. When writing libraries this is especially uncertain since you don't know the app's architecture.
The UI being global mutable state isn't actually true on all platforms (but is true on the web). And even then its possible to "hide" that to some degree in well designed frameworks.
Never say never, but half of your examples _shouldn't_ be globally mutable state.
In some other Python code, I have some global objects which manage some data, but few or none standalone global variables. There just doesn't seem to be much need.
You seem to be talking about accessing things through interfaces, so that one can change whatever is on the other side of the interface. For testing, for example.
I am referring to something that there is only one of, while the program is running, which holds state, and that all part of a program potentially have access to.
Even if a dependency is injected, if it has state, and all parts of the program could potentially access it, I would consider it global mutable state.
This is part of the problem with the 'don't use global mutable state' advice. If what you really mean is 'access state through interfaces', then that's what we should say.
So, to be concrete, in my use of the term, a file cache is global mutable state because
a) I don't want multiple caches, potentially with the same content, in my program. In fact, this would be bad enough that I'd like to be confident it can't accidentally happen. I understand that some caches don't need this guarantee, but some do.
b) All parts of my program that use a file, use the cache to retrieve it.
c) The contents (and therefore the behavior, most specifically the timing) of the cache depends on the history of the program up to that point.
However, my implementation, I access files through an interface, so it would be trivial to change implementation.
Dependency injection does not insulate you from the global mutable state. It is a way of managing containers for state (or other behavior). Those containers can have any boundaries within your program you choose, from global down to the most local (remember that 'global versus local' is a continuum, not a binary).
This Clojure library is doing so beautifully with immutability: https://github.com/stuartsierra/component
Yet each component is supposed to implement component/Lifecycle protocol... in other words, an interface.
And component is actually awful in practice. It may look neat in theory, but it's too simple and results in a mess not much better than it would be without it.
Not all parts of the program could potentially access it - it's injected where it's needed but only where it's needed, if other parts of the program wanted access to it you would have to change them so that it was injected there as well.
It makes little substantive difference whether the import is in code or in a separate declaration. Little substantive difference to whether the state is global and mutable, that is. Don't misunderstand me as claiming dependency injection is pointless.
Yes. Therefore you need to check every bit of data coming from user. You need to be ready to a lot of breakage, and need to code ways to recover for it. Working with external data is hard, need a lot of checking and exception handling. It is nightmare and you do not want to propagate this to every line of your code. So just do not create global state.
> a database is global mutable state, as is the DOM / UI.
And this also. You always need to expect something unexpected with global mutable state. DOM can be changed while your code is working with it. You should write code, that can notice such an inconvenience and be nice in this case. Or to write code that just works and do not breaks things, if something unexpected happened. Or code that makes unexpected impossible to happen. The same with databases.
I often miss transactions and checks on regular in-memory data. While it must be operative, and cannot be done out-of-process or even out-of-context, locks and dispatching is a mess at the end of the day, because correct paths are usually independent, and vice versa — conficting paths can simply fail or retry at the end, wasting cpu time, not developer's. Actually, good principles (i.e. explicit context, no globals, strict hierarchy, etc) are resembling transactions in a sense; you copy parts of state to worker(-s), delay updates into results and reintegrate these in dispatcher in a blocking manner. Only by hand.
One thing that stops me from doing everything in just sqlite is copying. You always copy to and copy back and every += becomes a copy operation. Right now (well for some background time) I'm thinking of the language that has the concept of transactions and relational algebra built right into the base types, but is not so cumbersome to write and/or poorly optimized like conventional database scripting. Mating rdbms internals with e.g. C-likes (as the common least) would be nice to test in real tasks.
Ps. I'm sure I'm missing some past info on that; if someone is aware of the details, you're welcome to share.
As of right now (for game projects) my strategy is to use source code generation to make the working language behave in a database-like fashion and emit a static result, rather than to engineer a complete dynamic schema with a scripting runtime and all the bells and whistles of a standalone database. The latter is both harder to build and lets me leverage fewer of the tools of the host environment.
I am curious if you've looked at C# and their LINQ stuff though as it sounds like it might check a lot of those boxes. I'm no C# expert myself, haven't used it for several years (since right before LINQ was introduced) so I'm not sure, but it looked to be along those lines when it was being proposed.
If global, mutable state is considered problematic when programming in the small (i.e. single program), why is it unquestioned when building much larger, more complicated systems that we also need to reason about?
Yes, hence file (and database) locking!
I think at a larger scale it becomes much harder to try and transform the system around immutable state. At the end of the day if most one needs to do is store CRUD items the easiest way forward is persist schema to a DB.
filesystem is basically a database with simpler interface, less guarantees and more ways to mess up.
internal global mutable state within your program is just a wild west. especially in multi-threaded environments.
The same goes to your filesystem, the key thing is: global as global access vs. global as "there is one of it". The same goes for the singleton pattern (which are terrible) vs. just having one of something.
Once you mutate the database, then the behaviour of any code anywhere that uses the DB, whether directly or through a module is potentially affected by the mutation.
Modularisation is great and all, but it doesn't make concurrency and cache-coherency problems go away. In fact it sometimes makes those things worse (or at least more opaque).
The internet is "global state" but you wouldn't call any code that makes HTTP requests global state because the "globality" is isolated inside your app.
Global can mean several things, my point was only that "globality" inside your app can (and should) be very constrainted while acknowledging some things are indeed global from your app's point of view. A good example of this is having a global "db" object vs. a DB instance passed to places.
This is more about whether or not your code should care about where it gets data or not. I think this (now classic) talk is a nice explanation about why your components should not "look for things" in the global but instead should be explicit about dependencies any why it's easier to reason about: https://www.youtube.com/watch?v=RlfLCWKxHJ0
That's why the design discussions about how to do stuff on such networks always centre on how to manage this global, distributed, mutable state. It really is hard stuff.
The nice thing, is that once you have done that design work, and are programming "in the small" to implement it, the individual processes can be stateless.
See for example Rich Hickey's Datomic [0].
[0] https://www.infoq.com/presentations/Datomic-Database-Value
Yes.
> If global, mutable state is considered problematic when programming in the small (i.e. single program), why is it unquestioned when building much larger, more complicated systems that we also need to reason about?
It is, and should be, considered problematic. The filesystem can mostly be avoided. The database should probably be rebuilt into something that's easier to reason about. See e.g. https://www.confluent.io/blog/turning-the-database-inside-ou...
Furthermore, if you deploy incorrect code, it could add JSON blobs to the database with the new schema before it crashes on the old schema. Now you've got two incompatible versions of the JSON blob in the DB at the same time, so rolling back to the old code will still be broken!
Software engineers deploying production apps have been dealing with these issues since we started using databases, but that doesn't mean that it's any easier or less error prone. It just means that we have to be more careful. And that care has a cost.
This cost isn't unquestioned for larger systems. It's a big part of the motivation for append-only databases, for example.
http://thesoftwarelife.blogspot.com/2010/12/append-only-data...
It's not unquestioned at all. See for instance, CQRS and event sourcing.
Mutable state is generally a problem, but undelimited mutable state is a serious headache.
oldEnvironment = getGlobalEnvironment()
SwitchToGlobalEnvironment("Foo")
- Do things that rely on the "foo" globals
SwitchToGlobalEnvironment(oldEnvironment)
Or, worse, functions like this: // NOTE: DO_STUFF assumes "FOO environment". Set FOO ENVIRONMENT before you call it.
def doStuff() {
- Do stuff that relies on foo environment
}
or: // NOTE: depends on the value of global FooBar. Adjust FooBar to make DoStuff2 behave how you want
def doStuff2() { ... }The solution is traditionally access restriction (OO) or immutability (FP), but it can also be - strict no aliasing rule.
Imagine if there were only 2 kinds of functions: pure functions (that can accept parameters have local cariables, and return values, but cannot change nor reference global state), and impure procedures (that cannot accept parameters and doesn't return anything, but can reference and modify global state, always starting from root object and going through all nested objects).
Then if you want to know ALL plaes which could have influenced or depended on the value root.objects.player.x - you simply search for that exact string.
You can start with all code using global state directly, and slowly move parts into pure functions. It can even be automated.
Game.getEntityById('player').applyDamage(20);
The bullet class then has access to all entities in the game and can mutate them. Global Mutable State, no? Barring the performance issues of doing this lookup dynamically, is that so bad?In your example - in the 'reduce' step - the implication is that the attack and debuff are handled in a deterministic way, regardless of their order? I feel like then that would require the logic for reducing all game commands needs to reside in the engine loop, which doesn't seem right to me.
The naive way is to give every object in your game an Update() then on each discreet step you iterate through the list of objects in your game and call Update(). This way lies madness because every object can interact with every other object from any Update() and you can never know where the changes are coming from.
The concept above is that you move up a layer of abstraction and you create a GameState object that contains all objects. You instead give each object a GenerateUpdates() method that returns a list of GameStateChanges. Thus to update the game, you take the GameState, iterate through each of the objects in GameState and get a list of GameStateChanges. You then pass both to either GenerateNewGameState (if game states are cheap) or ApplyChangesToGameState (if they are expensive). This reduces all of your mutations to one method and is much easier to update/debug/fix/understand.
Does that help?
This is actually a reliable way to construct most programs; it can aid significantly with reasoning, debugging, and performance. I try to build software this way whenever I can.
So for games - like for most non-trivial applications - some global mutable state is unavoidable. The task IMHO is not to eliminate it but to reduce it or at least restrict its "degree of globality". For example - even when you can't avoid global mutable state shared by threads within one client process, probably you can reduce the global mutable state shared by different client machines in a multiplayer match.
The gist of the idea is you would write _pure_ functions like this with an imperative style (example in Haskell)
bullet = do
player <- getEntityByID playerID
applyDamage 20 player
This will require some syntactic sugar (do notation in Haskell, for comprehension in Scala) to make it look procedure / imperative. However, the whole expression remains pure, and does not read or write to global state, it's just a data structure capable of expressing steps of mutations (and/or side effects).A separate part of the system (interpreter) will actually collect all such data structures and apply actual updates to global states in one place and in a principled way.
Some additional benefits includes easily implementing different interpreters (eg. for unit testing), adding "middlewares" to support things like logging and type safety on mutations.
I have no idea whether game programmers uses this pattern or not. However, it's very common in games to add a scripting language (LUA is a popular one) to express things like unit AI, dialog trees on top the underlying engine; which seems rather similar architecturally: adding a layer of indirection, use a expressive (just enough) DSL to encode the logic for mutations with only limited exposure to underlying global states.
A better resource that explores this idea can be found here: http://gameprogrammingpatterns.com/bytecode.html
An object shouldn't have access to independent objects, like a wall or another player.
I'd implement an observer pattern (or some other pattern) so you can safely decouple your bullet class say from the wall object.
UserAction -> bulletFiredMessage -> bullet fires and travels
-> wall receives message -> wall starts to poll if there was an impact
-> boss entity does the same
It's probably not a really good example, but you see state remains in objects.This obviously isn't true universally, and almost assuredly isn't meant to be; but, even interpreting it in the spirit in which it seems to be meant, surely the right translation is "you can't find bugs in a given code unit by reading only that code unit" or, more positively (?), "to find bugs in just one code unit, you have to read all the code."
Ideally you want to just read a few lines of code and say yes-or-no. Any need to read more should be because of genuine big picture stuff. For example:
Good: Yes, these five lines of code correctly parse the `fooTarget` flag, but I need to read more to understand if fooTarget is the right thing to parse.
Bad: Ok, we are parsing the `fooTarget` flag, but these five lines will only work right if the current locale has certain properties, is the locale somehow guaranteed at this point?
I know in ES6 closures inherit the scope they are declared in, but I don't think that's a required part of the concept is it?
Is x a "global mutable state" variable? If the above is the entirety of the program, then essentially yes. If you say "no, x is not global mutable state", then "global mutable state" is pretty meaningless in JS, since any script can be wrapped like above.