10 ms·
Global Mutable State
- shalabhc 9y agoIsn'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?
- JadeNB 9y ago> Isn't a database global mutable state? Or a filesystem? Yes, hence file (and database) locking!
- bsears 9y agoGlobal, 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.
- vikiomega9 9y agoThere 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.
- keymone 9y agodatabase 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.
- inglor 9y agoA 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.
- adrianratnapala 9y agoI 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).
- wiz21c 9y agoThe 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.
- deleted 9y ago[deleted]
- inglor 9y agoI'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 https://www.youtube.com/watch?v=RlfLCWKxHJ0
- adrianratnapala 9y agoYes 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.
- bcherny 9y agoDatabase state is not necessarily global or mutable. See for example Rich Hickey's Datomic [0]. [0] https://www.infoq.com/presentations/Datomic-Database-Value https://www.infoq.com/presentations/Datomic-Database-Value
- lmm 9y ago> 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-out-with-apache-samza/ https://www.confluent.io/blog/turning-the-database-inside-ou...
- novembermike 9y agoReality is global mutable state. You can't get away from it entirely, you can only isolate it.
- mightybyte 9y agoYes, 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-databases.html http://thesoftwarelife.blogspot.com/2010/12/append-only-data...
- tigershark 9y agoIf 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.
- ilyagr 9y agoMore 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.
- naasking 9y ago> 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.
- JadeNB 9y ago> 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."
- scarmig 9y agoMaybe 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.
- clusmore 9y ago>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
- elihu 9y agoOr a concurrent development team that modifies the shared global code-base haphazardly...
- adrianratnapala 9y agoYes. 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?
- deckarep 9y agoTry dealing with global mutable state in a multi-threaded scenario. This is when the real fun begins...
- jon_adler 9y agoWe are investigating using Scala and Akka (using actors) for concurrent processing. No locks are needed...
- kaeluka 9y agoUnless two actors share state, or use global mutable state.
- MereInterest 9y agoAbsolutely. 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.
- brlewis 9y agoDoesn't even have to be global.
- sago 9y agoI'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.
- lhnz 9y agoThe 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.
- fleitz 9y agoJavaScript is actually a better case than most languages as it doesn't support threads.
- kibwen 9y agoI 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).)
- yathern 9y agoI'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?
- deleted 9y ago[deleted]
- benaiah 9y agoYou 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.
- akmiller 9y agoWhat 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 https://www.youtube.com/watch?v=1PhArSujR_A
- benaiah 9y agoI'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.
- asow92 9y agoYou 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.
- xamuel 9y agoIt'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.
- deleted 9y ago[deleted]
- SCdF 9y agoHow 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?
- xamuel 9y agoConsider 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.
- fleitz 9y agoMost 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.
- olavk 9y agoThat'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.
- ajuc 9y agoIMHO 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.
- SCdF 9y agoOne 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.
- mquander 9y agoI 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.
- roywiggins 9y agoThe 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() { ... }
- ams6110 9y agoI 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?
- MereInterest 9y agoI've worked with a library in which the file currently being read/written was maintained in global state. Not fun at all.
- kahlonel 9y agoA 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".
- slavik81 9y agoGlobal 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.
- deleted 9y ago[deleted]
- throwaway613834 9y agoVery relevant (and, I think, excellent) article: http://misko.hevery.com/2008/08/17/singletons-are-pathological-liars/ http://misko.hevery.com/2008/08/17/singletons-are-pathologic...