6 ms·
Global state is nearly impossible to test in any decent automated fashion. When writing unit tests, globals are the bane of your existence. If you’re relying o
by bluejekyll 7y ago
Global state is nearly impossible to test in any decent automated fashion. When writing unit tests, globals are the bane of your existence.
If you’re relying on globals for passing data, they are also difficult to reason about in multithreaded code.
There are means by which you can share data, that data if instantiated at the code entry point, can be shared in such a way as to never need globals, and rely on decent patterns for sharing between code points.
Yes, there is a trade off in adding parameters to functions, references in classes, but these can be avoided by adopting patterns like inversion of control, etc.
Basically globals are a bad pattern because they make it hard to test and hard to reason about data access patterns.
- naikrovek 7y agoI don't understand. If you have a good understanding of the code you're writing, you won't put yourself into a position where globals cause problems unless you're being very stupid, and if you do, normal use of the program will detect those problems, right? Certainly bug reproduction steps and a debugger will figure out what's going on. You mentioned unit tests, and these are another thing I don't fully understand. Obviously testing your code is important, and automated tests are good. My beef with unit testing comes with the requirement that all methods and functions have multiple tests each for success and failure conditions, and that results in test code which outweighs tested code by several times. When you discover that the architecture you've been putting together isn't going to work, which is something that happens approximately 100% of the time if you're doing anything real, you now have (say) 5,000 lines of code that needs rework, and 50,000 lines of test code that need to be thrown away and rewritten. That is A LOT of effort to shove onto yourself to avoid a few global variables, to me. That's so much effort that many projects will just not make the change and ship software that they know is insufficient, and then they'll graft on whatever functionality can't be attained natively with the given architectural decisions rather than redesign. The ability to paralyze yourself with the weight of unit test code seems like an extremely high price to pay to avoid some global variables.
- NathanKP 7y ago> When you discover that the architecture you've been putting together isn't going to work One of the underappreciated benefits of unit tests is it quickly teaches you how to write good code. It turns out testable code is also code that tends to be well architected and doesn't need to be rewritten. Basically writing tests leads you to being a better programmer
- naikrovek 7y agoI have never witnessed that in 15 years of working at places which write unit tests. I've witnessed a LOT of unit tests which test nothing and manually return the pass/fail result desired so the indicators stay green.
- tasuki 7y ago> One of the underappreciated benefits of unit tests is it quickly teaches you how to write good code. It turns out testable code is also code that tends to be well architected and doesn't need to be rewritten. Basically writing tests leads you to being a better programmer In majority of situations, this holds (apart from the "doesn't need to be rewritten" part!). But there's a large minority of situations where it doesn't.
- giantDinosaur 7y agoUnit tests are perhaps good for instilling a decent sense of function decomposition, but make no mistake, you can go too far in this direction and not develop the sense of an integrated system. It's a hard problem to avoid, especially when starting out. That's one of the reasons I generally find type-driven development better for seeing how parts are actually interacting. Not to discount, testing, naturally, but I also prefer property based testing to unit for the same reason (i.e. a function can be a mini-system with relationships between internal values that may not be exposed with unit tests.)
- taeric 7y agoThis is only true in a case where you don't spin up and tear down your program per test case. And I don't want to defend globals. Globals are bad because they are just often used poorly. In large because they require you to think about the whole system as you make changes. Ironically, the best changes are done with the whole system in mind. Such that sometimes establishing a few core globals and some rules for how they will be treated can actually help your logic. So it really is a tradeoff. With a great slogan of "think globally, but act locally."
- pjc50 7y ago> This is only true in a case where you don't spin up and tear down your program per test case. Well, yes, but then the unit tests end up taking twenty minutes.
- nybble41 7y agoNot only that, you also can't trust that the test results will apply to any situation where the user doesn't restart the program after every action—i.e., to normal operation. Don't restart the program between tests. Randomize the order of the test cases between runs. Try running the same test multiple times on occasion.
- taeric 7y agoYou shouldn't have your tests artificially limit the life cycle of parts. Either for artificial reuse or artificial termination. To that end, if you have variables that live as long as your program, or longer, have your tests reflect that.
- TheOtherHobbes 7y agoIt's about scope. The "ideal" design one pattern is supposed to be separation of concerns - the devolution of performance and responsibility into units that can be built and tested independently. This is fine when that design pattern fits the domain. But some domains require global context, and it isn't useful or possible to strictly enforce separation - because you end up passing parameter bundles around and managing all those local scopes introduces more bugs than implementing a global context. Multithreading is a different issue, and is a different kind of domain requirement. If you need multithreading and have a global context, you have a very interesting problem to solve.
- speleding 7y agoI agree that globals are usually a bad pattern, but there are situations where judicious of globals is warranted and can actually improve readability. An example is small scripts, where the scope of the script limits the scope of the global. The overhead of an abstraction doesn't pay off in that case. Another example are "near constants" like a locale setting, an environment variable that gets detected once at startup, or a development feature flag. The "proper" way to structure those is to create a settings object and pass it to every function that needs them, but judicious use of a well-documented global can prevent a lot of boiler plate code. Of course, as soon as the code base needs to be touched by many devs, especially less experienced ones, it's safer to say "never do it" than "judiciously use", so I understand why most textbooks say this.
- clarry 7y ago> Another example are "near constants" like a locale setting, an environment variable that gets detected once at startup, or a development feature flag. The "proper" way to structure those is to create a settings object and pass it to every function that needs them, but judicious use of a well-documented global can prevent a lot of boiler plate code. In small programs, globals are ok, but in larger programs a better approach would be a global accessor that gives you read-only access: printf("%s\n", Environment()->Host); This doesn't require passing an object to every function, and the application still can't trample on these variables.