News for hackers. Stuff that matters.
Re:It's not what programming languages do, it's what they sh... by bluejekyll on Thursday March 26, 2020 @11:25PM (#22700203)
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.

Re:It's not what programming languages do, it's what they sh... by taeric on Friday March 27, 2020 @01:26AM (#22700616)
This 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."

Re:It's not what programming languages do, it's what they sh... by TheOtherHobbes on Friday March 27, 2020 @07:01AM (#22701853)
It'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.

Re:It's not what programming languages do, it's what they sh... by pjc50 on Friday March 27, 2020 @02:35AM (#22700810)
> 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.

Re:It's not what programming languages do, it's what they sh... by nybble41 on Friday March 27, 2020 @07:02PM (#22707679)
Not 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.

Re:It's not what programming languages do, it's what they sh... by taeric on Friday March 27, 2020 @11:53PM (#22709088)
You 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.

Re:It's not what programming languages do, it's what they sh... by speleding on Friday March 27, 2020 @05:31AM (#22701412)
I 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.

Re:It's not what programming languages do, it's what they sh... by clarry on Friday March 27, 2020 @09:48AM (#22702773)
> 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.
Re:It's not what programming languages do, it's what they sh... by naikrovek on Thursday March 26, 2020 @11:38PM (#22700257)
I 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.

Re:It's not what programming languages do, it's what they sh... by Others on Friday March 27, 2020 @12:01AM (#22700345)
I think that globals are not a problem when "you have a good [enough] understanding of the code you're writing." The problem is when code bases grow, references to globals can start to appear in lot of different places, and the exact use of a particular one can be hard to reason about. (Strictly talking about mutable global state here.)

As code bases grow, and developers come and go, eventually no one will have a "good [enough] understanding." Mutable global state is fundamentally hard to reason about since it can be changed at any time by any part of the program. When you first start out the codebase, you can just remember where all the usages are. But eventually that is not a good approach.

I consider the testing stuff orthogonal and muddying the issue. Mutable globals are hard to reason about, therefore they can make code hard to debug. Thus they should be avoided. No need to bring testing into the picture.

Re:It's not what programming languages do, it's what they sh... by kungtotte on Friday March 27, 2020 @02:20AM (#22700766)
I think most of the problems with globals can be solved at a language level. Immutable references to globals are practically never a problem, so if your language forced you to explicitly mutably borrow a reference to a global variable you force the programmer to think about every instance of code where they are modifying the global.

This also enables tooling to for example syntax highlight these things differently. An immutable global looks like any other variable, but a mutable global is bold red.

You bet your ass that people will think about whether they really need it mutable in that case, and they'll know everywhere it's made mutable and therefore error prone.

Again this comes back to shepherding. Globals in Rust aren't the same as globals in C++ because the languages shepherd you differently.

Re:It's not what programming languages do, it's what they sh... by amylene on Friday March 27, 2020 @02:39AM (#22700820)
Remember the old phrase, “imagine the person who maintains your code is an axe wielding murderer who has your home address”?
Re:It's not what programming languages do, it's what they sh... by tonyarkles on Friday March 27, 2020 @12:02AM (#22700348)
One of the problems I’ve encountered in the wild is that globals often mean that you have to check the entire program when things go weird. You’re right: if you have a complete understanding of the entire codebase, then it probably won’t be an issue. But software grows and ages; globals won’t hurt you (much) early on in the project, but they start to in the long term. Your coworker modifies it in a place where you don’t realize it’s being modified, and things that worked fine yesterday stop working. The coworker might be you when you’re tired :)
Re:It's not what programming languages do, it's what they sh... by unlinked_dll on Thursday March 26, 2020 @11:58PM (#22700334)
Not sure what your gripe on testing has to do with what the comment is saying. Globals make testing hard.

The simple answer is that globals are expensive. Literally, they cost a lot of money. They introduce bugs that are harder to find, reproduce, and fix. That means introducing a global is a high risk, since it's increasing the expected value of your non recoverable engineering costs.

Rejecting globals is about lowering risk and cost because it's so easy to not use them and toss them out of code review, and it's really easy to work around that limitation.

Gonna remove my more uncivil remark. Basically relying on bug reports and debugging is the software equivalent of waiting for your engine to seize before you change the oil.

Re:It's not what programming languages do, it's what they sh... by NathanKP on Thursday March 26, 2020 @11:45PM (#22700288)
> 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

Re:It's not what programming languages do, it's what they sh... by giantDinosaur on Friday March 27, 2020 @04:34AM (#22701233)
Unit 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.)

Re:It's not what programming languages do, it's what they sh... by sgift on Friday March 27, 2020 @05:19AM (#22701371)
That's a myth. It teaches you to write code which is easily unit testable. That may be a better architecture than the one you would have used, but often it's just a different architecture, sometimes even markedly worse.

I have seen far too many code bases with simple things chopped up beyond recognition to make the code unit testable.

Re:It's not what programming languages do, it's what they sh... by anthonyrstevens on Friday March 27, 2020 @04:01PM (#22706270)
"[S]imple things chopped up beyond recognition" sounds like a hyperbolic argument to me. What is an example? When is the maxim "A function should do one thing well" not applicable?
Re:It's not what programming languages do, it's what they sh... by tasuki on Friday March 27, 2020 @12:44AM (#22700495)
> 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.

Re:It's not what programming languages do, it's what they sh... by naikrovek on Friday March 27, 2020 @12:08AM (#22700367)
I 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.
Re:It's not what programming languages do, it's what they sh... by NathanKP on Friday March 27, 2020 @12:33AM (#22700458)
That's very unfortunate.
Re:It's not what programming languages do, it's what they sh... by mdpopescu on Friday March 27, 2020 @08:52AM (#22702448)
I think the "leave the site better than you found it" advice applies here. Whenever you need to touch a piece of code, write the proper tests (hell, add some fuzzy testing if you don't want to write them by hand) and then improve that code.
Re:It's not what programming languages do, it's what they sh... by LandR on Friday March 27, 2020 @11:20AM (#22703550)
Try reasoning about a code base that is 5million+ LOC, tens of thousands or hundreds of thousands of functions and has 300+ people working on it. THis

>If you have a good understanding of the code you're writing, you won't put yourself into a position where globals cause problems

Becomes basically impossible.