Hacker News new | past | comments | ask | show | jobs | submit login
Global Mutable State (dev.to)
130 points by bhalp1 on Sept 19, 2017 | hide | past | favorite | 113 comments



One of the most confusing things I find with web development (which I'm mostly new to, coming from a backend background) is that everyone seems entirely happy with JS (or more accurately the DOM?) and CSS being giant mutable state buckets.

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.


Essentially every single aspect of the web is terrible. It was designed as a static document presentation system with hyperlinks. And for years and years and years, no matter how much interactivity was shoehorned into this system, the standards bodies absolutely refused to make any design decisions based on the absurd notion that it was an application platform. They have only changed this view very recently, and the consequences of their decisions made to optimize the creation of static documents with hyperlinks will continue to be felt for a decade probably.

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.


Had the web been designed that way it probably would have died an early death. Back then we didn't have gigabytes of RAM, fat pipes, and an abundance of cores to run interpreted code that is shipped from hundreds of kilometers away.


I don't think it's true that everyone seems happy with this. In the past 10 years culture has shifted dramatically towards encapsulating and minimizing JS state (modules, moving away from imperative jQuery code and toward MVC, React) and, to your other point, moderately towards more strict CSS organizational techniques (OOCSS, SMACSS, BEM).

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.


But the idea of React is to have central application-level global mutable state, instead of jQuery's lots of fragments of local state attached to DOM nodes ($(...).data) or kept in callback closures, CPS-style.

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.


I think the single state object in React/Redux is more an implementation detail than anything. Using regular js variables would allow free mutation for everyone at any place, so a new "variable system" must be created by encapsulating all state in an object.

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.


For styling, I'd look into CSS in JS, specifically JSS. Not only are all selectors scoped, you can create functions that return the proper styling. Frankly I think web developers as a whole need to rethink CSS and/or any paradigm based on traditional CSS like SASS/SCSS. We shouldn't need messy global variables like classes and ids to change styling.

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.


Thanks for mentioning JSS. I'd never come across it before but it looks very interesting.


I'm currently using it in a pretty large project and it's been awesome. Having the CSS related to the component in the component is really nice (which is found in other frameworks). When refactoring you don't have to wonder what this class could be tied to since it's all right there. You also benefit from dead code elimination so unused CSS classes get removed.


Yes it's really quite amazing that browser development has gained the traction it has, considering what a terrible environment it is to work in. I think its a combination of a lot of devs never having done anything else, so they don't know better, and also that removing the friction of deploying and configuring native apps was worth all of that.


* lowest barrier to entry of any platform

* 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...


Right... when was the last time you USED View Source on something done in JS and actually used on a production site? Sandboxing from the environment also has its upsides and its downsides. What people actually want applications to DO is deal with their information. And sandboxing the host means they lose any semblance of control over that data, leading to it getting monitored, mined, etc. And it doesn't solve any of the actual problems. It just changes the target while simultaneously making those targets SO much juicier. Want the personal info of 143 million Americans? Hack the web app of a credit company, no need to hunt down and break into 143 million different machines! And the users won't even know if the data was stolen unless a company volunteers the information! Sometimes they even do!

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.


> Right... when was the last time you USED View Source on something done in JS and actually used on a production site?

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...


> when was the last time you USED View Source on something done in JS and actually used on a production site

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?


My understanding of this argument, as best as I'm able to state it, is approximately: Claims that view-source is a significant virtue of the web are nostalgic and misguided, as demonstrated by the fact that it's difficult and rare today to find anything usable or valuable by reading the javascript source of major websites like facebook, especially by amateurs.

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.


Sure large sites with extraordinarily valuable IP make it hard to view source and learn anything. However other smaller sites are a wealth of wisdom. Just the other day for example I inspected the new KIKK website to see how they managed to keep their website silky smooth on mobile whilst cramming quite a few transitions/animations in.

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.


The discussion was about why the web became a successful platform in the first place. There is no doubt "view-source" was a very significant factor in this. This is basically how the first generation of web devs learned the craft.


Why is it amazing? A new platform delivers enormously simpler and cheaper development and deployment of cross-platform applications. Why is is surprising people adopt this platform?


Definitely leaning towards the latter IMO. That stuff was a nightmare.


> One of the most confusing things I find with web development (which I'm mostly new to, coming from a backend background) is that everyone seems entirely happy with JS (or more accurately the DOM?) and CSS being giant mutable state buckets.

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...


I don't think people are (were) actually happy with it: Hence the immutable/FRP-ish UIs of React and similar frameworks. Of course they still have to cope with the realities of web browsers so there's still some ugliness in there, but the awfulness is mostly contained.

(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.)


> 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.

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.


> This is solved with shadow DOM.

http://caniuse.com/#feat=shadowdom


You can use a polyfill.


> One of the most confusing things I find with web development (which I'm mostly new to, coming from a backend background) is that everyone seems entirely happy with JS (or more accurately the DOM?) and CSS being giant mutable state buckets.

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.


Check out Tachyons and similar CSS libraries which use CSS classes to essentially define inline styles efficiently. When you are making components, this can work out well in containing potential interactions of CSS. http://tachyons.io/

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.


Its difficult to get front-end developers to scope their CSS properly and separate UI components into non-conflicting separate modules.

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.


Are there best practices for scoping CSS correctly? I seem to reinvent the wheel every time I make a new website.



https://github.com/css-modules/css-modules takes care of it all for you.


You're mostly right.

Just know that things are fantastically much better than 10 years ago!


It's unfortunate that mutable global state is the default approach to web development, but it's certainly not true that everybody is happy with it. We're actually in a golden age of web development right now driven mostly by functional approaches to web development that adhere to the principles of minimizing global state, sensibly managing what global state is necessary by means of treating it effectively as an immutable collection, and defaulting to pure functions where-ever possible and limiting in-place mutation to hot code paths, while still being mindful of containing those side-effects to within lower-level function boundaries.

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.


I'm a bit sceptical about the ragging on global mutable state, given how useful it is (in my use cases, for resource management, e.g. connection pooling, caching, JIT compiling, file loading).

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.


Connection pools, resource caches, file loads (a resource cache) have no need to be global. If you inject your dependencies (also known as partial application in functional languages) you will find it far easier to test things in isolation, to decouple modules, and to refactor your software.

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.


One of us is using terms wrong, because we're talking about different things.

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).


> 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.

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.


s/injected/imported

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.


You do not need interfaces for dependency injection and it can also be done without global mutable state.

This Clojure library is doing so beautifully with immutability: https://github.com/stuartsierra/component


> You do not need interfaces for dependency injection

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.


Looking at my modern code on Github (not the stuff from the 1970s and 1980s I've revived), few projects have any global mutable variables at all. There are a few that are assigned once at startup. There's a timestamp. In Javascript work, of course, there's the DOM and other state that belongs to the browser.

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.


I agree that going to the extreme of 100% eliminating global mutable state is going to fail for practical purposes, but we don't need to go that far to benefit.

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).)


I agree.

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.


In many languages, variables are not necessarily state. Global variables are fine if they're global constants which can not (not just 'should not') be modified.


You really never use singletons or other static global objects? I use them fairly often, mostly in game dev.


Game dev is different than back-end server dev, is different from front-end web or desktop dev.

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.


Game developers don't write tests. They pick up habits along the way and keep them for a long time. The code might be crap, but they know their crap very well and can debug it. There is less need for tests because most code never rarely refactored or being touched at all. It's already in the familiar style, why change anything?


When I started programming I remember just a mess of line numbers and GOTO statements in which there was no global variable at all... because literally everything was global. I like much more my code now with glorified singletons called IoC containers, extensions methods that are just global static methods and all the rest of the wagons... ...just joking, I'm trying to write more F# now to finally complete my journey procedural->object oriented->functional


> If there is no user, then the external data the program is interacting with is often global mutable state

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.


Databases have proper context isolation and conflict detection (constraints, triggers) at least. You cannot break the invariants of a properly designed database (though some effort is required to create one).

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.


I previously worked on an extremely large (in whatever sense you wish to look at, size of codebase, number of users, transaction volume, whatever) system that was originally written in a language like you describe: Pro*C. It basically permitted in-lining SQL with your C code. It might've not been too bad on small systems, but shared Cs scaling problems. It was difficult to track where database changes were actually coming from, but I don't know of any other solution that would have made that easier. If you have a dozen applications across tens of millions of lines of code, all of which could potentially be responsible (or maybe whatever wrote the value they based their change off of was actually the problem...) things get hairy.

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.


Integrated language+database solutions are a recurring motivator for new one-off languages: Pick System and FoxPro are classic examples of writing around a custom language vs "scripting" inside SQL. Applications with custom database-like systems such as game engines also frequently lean in this direction(UnrealScript, GDScript). The main difficulty is in the practicals of getting all of the desired functionality into a useful package - not the theoretical aspects.

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.


The global mutable state isn't the problem, of course, it's any of your code that uses it. Minimize that code and you're done. And yes you are correct about all of the things you referred to as 'global mutable state'. They all are exactly that. They are very useful. People use software to do changes to that global mutable state, and that global mutable state is literally the only reason the software exists in the first place. It is the primary responsibility of software to manage it. And as with anything you need to care for, the less you have to manipulate it the better off everyone will be.


Basically you want to push global state and mutability to the boundaries of your program, where it connects to its environment.


So you feel your program is better off for having only one connection pool and being unable to use another?

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)


Not really, even in my worse app I never wrote anything in the face of a user... And you can't treat a user as a global mutable state if your code doesn't directly mutate it. I obviously agree with the concept that he is a very good example for an external system on which you have no control.


I agree, but this doesn't give much guidance about when global state is okay.

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.


Not to mention the Internet. There will always be some external mutable state. At best you can view other nodes in a computer network as actor objects.


The thing is, in JavaScript, unless you are extremely rigid about how you program, it is very easy to cause bugs.

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.


JavaScript is actually a better case than most languages as it doesn't support threads.


Isn't a database global mutable state? Or a filesystem?

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 such things are global mutable state. Worse yet, for big systems "global" means "global to an earth-wide network", not just to a single process.

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.


A database is only global and mutable if any part of your code can create it. If you isolate the database access to one module and then force other modules to interact with it through instances of that module in a controlled way - it's not global (but certainly mutable).

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.


I don't get this.

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).


I'll try to elaborate, global _app_ state is different from global state "in the wild".

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


The database (at least the relational ones (I don't know the nosql thing)) is actually a very well behaved global state. It gives you guarantees about its mutability which you often don't have in your own code (well, in mine at least :-)). Moreover, the transactional nature of those DB actually percolates in many places in the code and makes it more reliable. So I'd say the database is a very special mutable thing, it's not as general as the usual proposal of GMS and therefore, it's much less dangerous.


Yes, databases and filesystems are global mutable state (at least if you do anything other than append to them). And they also require vastly more care. For instance, if you store a JSON blob in your database, you have to be VERY careful with how you change it over time. If you make a backwards-incompatible change to the schema without realizing it, you break. A likely response is to advocate better testing, but you need a special kind of testing to detect this problem. Plain old unit tests won't cut it because it's not about whether your parsing and rendering code at any given point in time is correct. It's about whether the parsing code from today's commit is compatible with the rendering code from the previously deployed commit!

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...


Database state is not necessarily global or mutable.

See for example Rich Hickey's Datomic [0].

[0] https://www.infoq.com/presentations/Datomic-Database-Value


Global, mutable state is not necessarily a bad thing, it just needs to be managed very carefully (like through a database!) so all your mutations are routed through a single source of truth.


There are patterns around this. For example, persistent data structures and application patterns such as event sourcing. Design concepts like bounded contexts and microservices also count.

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.


database is sufficiently isolated, provides all sorts of safe mechanics to deal with state and people still mess it up.

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.


More generally, every computer eventually has to interact with the outside world, which is global and mutable. Human mind have a much easier time dealing with things which are as immutable as possible. For instance, a physical law like the Newton's law of gravity is immutable. The variables within it are mutable, but there's only a few of them -- they would be local in a function that computed the force of gravity. Without such laws, it would be very difficult to reason about physics.


Reality is global mutable state. You can't get away from it entirely, you can only isolate it.


If in your case is a global mutable state then most probably there is a problem. In my experience, most of the time, only a certain piece of code changes a certain table. If in your case every module can indiscriminately mutate every table I warmly suggest to mutate your design. In case of distributed applications in which you are mutating very frequently from multiple serves the same tables then don't forget to use transactions. In case you need to do it by design and you don't explicitly need transactions then use some in-memory cache backed by a db, with distributed locks when necessary. Someone already did the really hard work for you, no need to reinvent the wheel.


> Isn't a database global mutable state? Or a filesystem?

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...


> why is it unquestioned when building much larger, more complicated systems that we also need to reason about?

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.


> Isn't a database global mutable state? Or a filesystem?

Yes, hence file (and database) locking!


The absolute worst type of global mutable state I've had to deal with personally follows this antipattern:

    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() { ... }


I suppose you never worked with old ASP or PHP apps where the first thing every page did was throw every form field value into the global Session object?


I've worked with a library in which the file currently being read/written was maintained in global state. Not fun at all.


IMHO the main problem with mutable global state is - you don't know what caused it to have the value it has.

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.


I'm working on a toy game engine for fun. What about the pattern of having a globally accessible list of game entities? EG when a bullet hits an object do (psuedocode)

  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?


You can have that kind of flat, global structure without mutability. One way to do that would be to collect all actions done in each frame (instead of mutating) and then apply them all to create a new frame in a single step (a "reduce"). Then you call the game loop function with that frame to create another frame, etc. The issue isn't just global state, it's global and mutable state. Say that your game has a debuff that reduces the damage of an enemy, and your player debuffs an enemy in the same frame that it attacks him. If you're mutating everything, it can be very difficult to figure out or specify what order things like that happen in, since anything can mutate anything else at any time (concurrency makes this much worse). If you're using mutable state, there's no way to tell all the places that could be changing the enemy's damage, because it could be anywhere in the program. If you're passing everything through as arguments, it's very easy to see exactly where state changes occur and make code modifications in isolation without worrying about affecting unrelated behavior.


What you describe above is similar to what Carmack discussed at his QuakeCon keynote several years back when toying with a game engine on Haskell. Very interesting talk, https://www.youtube.com/watch?v=1PhArSujR_A


I've watched that, and it almost certainly contributed to my comment above. Thanks for linking it for others to enjoy - it's an A+ watch IMO.


That's an interesting concept - I'm not sure I'm fully understanding though.

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.


Background. There are two ways gamers think about games "real-time" and turn-based. The question about a bullet is most likely referring to what gamers would call a "real-time" game. The thing is, we programmers are forced to make the game happen in discreet steps. There are multiple ways to accomplish this.

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?


From what I understand, it is more that by collecting events that occur in a frame and then applying them on the next. It is easier to debug since you can print out the events on every frame. Which if there is a bug, makes it easier since you'll be able to see the events and the order they came in on.


Determinism is indeed a major benefit of the structure I described. I'm curious about your objection, though - where else could the logic be other than in the game loop? In this architecture, your game loop is basically three steps - collect actions, reduce actions, render, repeat.


> One way to do that would be to collect all actions done in each frame (instead of mutating) and then apply them all to create a new frame in a single step (a "reduce")

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.


In FP world, there's a somewhat advanced solution to this, Free Monad DSL/Interpreter pattern. I first found out about it in this talk: [https://www.youtube.com/watch?v=fU8eKoakd6o](https://www.you.... There's an example of a pure functional game ai DSL in it.

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


Well, it's kinda bad idea for the bullet to have access to everything. It can lead to a lot of mess later on. Global state is not your problem here.

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.


The fact that there logically exists a list of game entities, and that all threads are required to have a consistent view of these entities at any point in time and are allowed to mutate them, is a global mutable state.

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.


I don't see this being a big deal with games. When you want to e.g. introduce power-ups that have complex interactions between players, bullets, enemies and the physic system you can't really avoid giving all your entities access to each other for flexibility. Maybe you could use immutable data while avoiding memory management issues but what are you gaining? For all rules of thumb, you need to weigh up the benefit with the cost.


The particular example of game engines is where many laws of clean code break down. The tradeoff to performance is usually not worth it to get immutable data structures, AoS and all those niceties. Instead you make singletons, SoA, collections of mutable entities and so on. And that's fine, as long as you have good performance reason to make that tradeoff.


Try dealing with global mutable state in a multi-threaded scenario. This is when the real fun begins...


Absolutely. I once hunted down a segfault that happened because one thread was closing a GUI window while another thread was loading a deserialization library. Not fun at all.


We are investigating using Scala and Akka (using actors) for concurrent processing. No locks are needed...


Unless two actors share state, or use global mutable state.


Doesn't even have to be global.


You can't build products without mutable state. How far should we go when we reason about global mutable state? Do we go as far as the database layer? Computers are state machines and I don't know why we're so eager to get away from that. Separation of concerns and interfaces/protocols help, but somewhere down the line, it's accessible, mutable, and it's stateful.


> When people say "it's hard to reason about", what they really mean is "it's got bugs and you can't tell by reading the code".

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."


Maybe for a sufficiently smart person, you can find all the bugs by reading all the code. But if you've got a concurrent program that modifies global state haphazardly, a sufficiently smart person doesn't exist.


>There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. -Tony Hoare


Or a concurrent development team that modifies the shared global code-base haphazardly...


Yes. But saying "code unit" makes it seem reasonable to read a module or package (or whatever it's called in your language).

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?


Global mutable state is just the worst-case of a more general problem. Unnecessary scope and mutability make programs harder to reason about because when trying to understand code they require you to investigate things that you might otherwise be able to instantly rule out as impossible.


Very relevant (and, I think, excellent) article: http://misko.hevery.com/2008/08/17/singletons-are-pathologic...


A great way to get rid of global data (mutable or not) is to write testable code, and then write unit tests. I'm not a big fan of TDD or unit testing, but it does force you to think in terms of "contexts".


It's funny how closures are in-fashion and global mutable state is out-of-fashion, when the latter is really a special case of the former.


Most closures don't reference global mutable state. Generally they take immutable copies and you have to tell the compiler that you want mutable state which is generally limited to the scope of the function the closure appears in.


That's like saying global variables are just a special case of variables, so it is "funny" global variables are discouraged while local variables and parameters are accepted.


How are they related sorry?

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?


Consider this ES6 program: (function(){let x=0; let f=()=>{x=1}; f(); console.log(x);})()

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.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: