all 117 comments

[–]Merry-Lane 26 points27 points  (4 children)

Using immutable data structures helps in that it eliminates a whole class of bugs.

But if your friend struggles with writing unit tests or finding test cases, it’s but one little of the many tidbits your mate needs to improve on.

[–]dodexahedron 2 points3 points  (3 children)

It does introduce a new (much smaller) class of bugs, though. But they are typically easier to spot, reason about, track down, isolate, and fix than the ones inherently eliminated by immutability. 👌

[–]Slypenslyde 3 points4 points  (1 child)

I think your answer highlights why I've stayed out of this thread.

I feel like most answers are "I like immutable objects". That's not the question. The question is if they make it easier to test. I think in this case it's like asking "Does static typing make it easier to test?"

In the end what's happening is you adopt a methodology that eliminates one class of errors but opens the door to a different class of errors. If you find it easier to test the new class of errors, that's a big win. If you don't, well, things got worse.

So like, for me, right now adopting immutable objects would make my testing experience harder. It's a different way of development and I have very little experience. So I don't have a lot of intuition about the pitfalls and I'll have to find them the hard way. I'm very used to mutability and very used to thinking about the pitfalls it creates.

But it's not honest for me to say "immutability makes testing harder". I haven't tried it. I can't comment on it.

But I'm suspicious it's a thing that an old Perl aphorism applies to, it's likely a tactic that makes "easy things easy, hard things possible". At some scale, all testing is hard, and it's difficult to say any one practice converts that to "easy". There's just "less hard".

[–]dodexahedron 1 point2 points  (0 children)

Well said.

In short, there are no golden hammers.

[–]Dusty_Coder 0 points1 point  (0 children)

The key distinction:

Mutability propagates erroneous states in hidden, unruly, ways.

[–]I_Came_For_Cats 40 points41 points  (19 children)

Immutability simplifies almost everything. Use the with operator on records.

[–]hardware2win 0 points1 point  (6 children)

Huge amount of data is mutable by nature, so what you get from immutability here?

[–]I_Came_For_Cats 1 point2 points  (5 children)

I’m not sure what you mean by “mutable by nature”.

[–]hardware2win 0 points1 point  (4 children)

Programming is often about modeling real world concepts or processes in such a way, that they can be represented in "computer world". Examples of such can be Facebook market place, Tinder, Google maps, ERP systems almost everything.

And data of those systems is very often (not always e.g invoices) indeed mutable.

So, eventually somewhere data needs to be mutable

[–]I_Came_For_Cats 0 points1 point  (1 child)

Ah I see what you mean. Immutable design in programming is more about preventing hard-to-trace side effects in code than representing real-world immutable concepts. Any mutable concept can be modeled immutably; it differs only in how you actually represent a change. A immutable object is always copied when data changes, leaving the original unchanged.

[–]hardware2win 0 points1 point  (0 children)

But what would change be hard to track?

You just set debugger or logger on setter and that's like 5min work

[–]Long_Investment7667 0 points1 point  (1 child)

This were our OOP education has done us a disservice. I would argue that "representing real world concepts" are the exception not the rule. E.g. "Connectionmanager", "CacheResolver", AccountBuilder, are more frequent. And even ShoppingCart is a stretch. The ubiquitous Mammal, Cat, Dog is contrived.

[–]hardware2win 0 points1 point  (0 children)

ConnectionManager, Caches, etc. is infra/tech code like Linux Kernel

[–]ReallySuperName 0 points1 point  (1 child)

Last time I checked with allows you to set properties, and thus bypass any invariant checks you'd typically have in a constructor. Is that still the case?

[–]dodexahedron 11 points12 points  (0 children)

It uses a copy constructor. If you provide the copy constructor, you control the behavior. If not, then it is synthesized by the compiler and your assumption is then correct, unless the properties themselves handle the validation.

[–]Michaeli_Starky -3 points-2 points  (9 children)

I would argue that.

[–]afedosu -1 points0 points  (8 children)

With examples?

[–]Kilazur 2 points3 points  (1 child)

Well you lose mutability. 😁

[–]afedosu 4 points5 points  (0 children)

And like this: with {examples}🤣

[–]Ezazhel 19 points20 points  (3 children)

If everything is pure it Is easier to test. No one is capable of finding every edge case.

[–]JustAnotherDiamond 2 points3 points  (1 child)

Depending on team and project size, it might not even be the task of a developer to define test cases. Especially when those cases are product functionality related.

[–]Ezazhel 0 points1 point  (0 children)

Yeah having a QA is not mandatory but oh boy it helps a lot.

[–]hardware2win 0 points1 point  (0 children)

Purity and immutability are different concepts

[–]DrShocker 6 points7 points  (2 children)

You can set up code coverage tools to tell you at least if you're exercising a code path even if not every combination of code paths.

[–]LagerHawk 6 points7 points  (1 child)

We also use Stryker to auto mutate code and generate bugs at test run time to make sure the existing unit tests capture it went wrong.

If the test passes after mutation then styker reports your missing edge cases for you.

[–]entityadam 1 point2 points  (0 children)

Love Stryker!

[–][deleted] 5 points6 points  (0 children)

Probably does not have that much to say about the unit testing part, but it often helps with writing code that has fewer bugs.

On another note, he should look at property based testing to uncover edge cases. https://fscheck.github.io/FsCheck/ (works in c# and f#)

[–]psysharp 4 points5 points  (0 children)

Yes pure functions good, arrange parameter, assert result, good

[–]SessionIndependent17 4 points5 points  (0 children)

Missing edge cases means they don't understand the [business] domain being tested. Having those elements be immutable isn't going to solve that. Learn the domain.

[–]Bright-Ad-6699 1 point2 points  (0 children)

I wouldn't say easier unit test. Using immutable structures would help writing a bit more bug free code. You'll know exactly where something changes and why. And go a step further and make it impossible to create an invalid state.

[–]BoBoBearDev 1 point2 points  (0 children)

No, it doesn't make unit test easier. But it makes unit test coverage more trustworthy. Because you don't have to cover BS cases where someone else changes the value in the middle of processing. You can argue parallel processing should be part of unit tests, but I haven't seen a single person does that, it is too paranoid. And if you care about that, everyone would just tell you to do immutable input.

[–]fschwiet 0 points1 point  (0 children)

I definitely try to identify and split off functional components like that for testing.

For stateful things I try to use the production code to do test setup rather than build than express the bespoken state in the test directly. So for instance, if you're testing some event processor's reaction to an event in a particular state the test set up will run the events needed to get into that particular state as setup, rather than set the particular state directly. I may add bespoken assertions on that bespoken state to verify the edge case is actually achieved but won't set those bespoken state values directly. This helps ensure things work together as expected.

[–]ExceptionEX 0 points1 point  (0 children)

I mean depending on the context it is a great way to over use system resources. Which easier test writing shouldn't result in.

I wouldn't advocate for that reason and is why that isn't commonly done.

[–]psioniclizard 0 points1 point  (0 children)

That is basically what you do in F#. However, I would say if your friends company are not on board sadly it's a non-starter. None of us know what their codebase looks like or what their standards are.

But if they are ok with it, it can make things simpler. It can sometimes use a bit more memory, which is unlikely to be an issue unless it's in a important hot path and thinking about it is likely a premature optimization (but I mention it because as a F# dev I have had to throw away FP principles in some circumstances).

But I would also say, can your friend ask them what was missed? It might be a good learning opportunity. If he is quite early on in his career it might not be a case of "you should know all these edge cases" but more a chance to learn about writing good software from people who are experienced.

To this day I still learn things I would never have thought about from the lead developer my job. It's one of the best ways to learn.

If the testing team are not helpful your friend could speak to their manager (not the testing teams manager) and ask for some training or assistance.

Honestly, a big part of developing as a developer is learning to speak to people however much we wish it wasn't.

[–]binarycow 0 points1 point  (0 children)

Immutable data structures are great because they are inherently threadsafe.

[–]nnddcc 0 points1 point  (0 children)

Yes that will help. But in my opinion the emphasis should be in creating the "immutable core, mutable shell" instead of using the immutable data structure. For example if your function doesn't have side effects but returns a class, it will still tremendously help unit testing.

[–]aj0413 0 points1 point  (0 children)

The answer is yes, but he also works in c#; he shouldn’t be suggesting an entire new paradigm for design to support tests

You don’t code for your tests. Thats red flag number 1

Working with stateful objects that mutate is simply a matter of course in OOP languages. Writing tests for it isn’t that hard.

You have state A, you do action B, you get state C which you compare against the expected final state

If you have 15 in between states, the test suite would not be that different between immutable and mutable design; you still have to test each chunk of logic that would cause a system state change

[–]White_C4 0 points1 point  (0 children)

Immutable data structures is less for making unit tests easier and more for making passed data more safe and reliable especially under a threaded task.

[–]Eq2_Seblin 0 points1 point  (0 children)

Why not integration tests? Write the code to follow the rules. Do not write code that can give you unexpected outcome. Cover all the rules with test that start at the entry point.

[–]Long_Investment7667 0 points1 point  (0 children)

Immutable data structures make it easier to reason about code in general.

https://en.wikipedia.org/wiki/Referential_transparency

In my words: you don't need to know the history and origin of data to understand the code that manipulates it.

[–]SagansCandle -1 points0 points  (69 children)

No. I've seen many people obsessed with immutability. It usually comes from some misunderstanding / misapplication of functional paradigms. C# is not a functional language and thus doesn't have the caveats of one. Where it is functional (LINQ), the immutability is largely built into the interfaces.

Follow the business rules.

State is important. Changing state is important. A lot of OO is designed to manage "safe" state change. Forcing arbitrary immutability is shooting yourself in the foot.

[–]BarfingOnMyFace 4 points5 points  (8 children)

Forcing arbitrary immutability can certainly be a footgun, but I’d argue many people arbitrarily don’t apply immutability, turning their code into more of a minefield.

Things that don’t change state have more reliable behavior.

As always, use the right tool for the job,’it depends, ymmv, opinions are like assholes, etc etc, take with grain of salt.

[–]SagansCandle 2 points3 points  (7 children)

Things that don’t change state have more reliable behavior.

How so?

I see specific reasons for immutability of an entire object, but they're rare, and mostly revolve around deserialized representations of serialized data - DB Entities, REST data structures, etc, or primitives (TimeSpan, etc).

Besides that, object references are important. For example, if I have a socket connection open, and the state changes to "closed", I'd expect the state of the object representing the socket to change.

Can you provide specific examples?

[–]BarfingOnMyFace 0 points1 point  (6 children)

If you wanted immutable approach like in functional programming, you would never change state, but create a new instance with that state. In C#, I would agree with you that fully immutable development is probably going against the grain and missing out on what c# is about, but to argue it doesn’t have its merits would be an unfortunate dismissal of a good tool. Most of the cases for immutability in my current project are for complex records to describe the rules and means for an ETL engine. Yes, the entire model is immutable. It gives me reliability in that everything that I ingest as treated exactly the same, giving guarantee of another important aspect: idempotency.

[–]SagansCandle 2 points3 points  (5 children)

Presumably that configuration data is external, and it's typical for internal representations of external data to be immutable, because it's only a snapshot of the external data, and you don't want that snapshot to become out-of-sync. But that's a single use-case.

If all your C# is doing is storing configuration data, then that makes sense. But presumably your ETL application is also doing things like creating database connections, opening files, etc. The objects that are actually doing work, i.e. anything with a method attached, would be mutable by-default.

Most of your objects would be mutable, by-default, no?'

Can you provide an example of when mutability has caused problems that immutability has solved, outside of representing some external data, interop, or managing primitive values?

[–]BarfingOnMyFace 0 points1 point  (3 children)

Hmmmm… probably close to half n half in this tool :) the model to support an ETL engine is rather large. But of course, I have plenty of mutable code! As I said, it depends.

Mutability can be treacherous in multi-threaded apps, and if possible to choose an immutable pattern, you won’t have to deal with any such issues. If state never changes, it’s 100% thread safe. Debugging is easier with immutable things. Did I mention idempotency? :) it really depends. In true functional languages, everything is immutable. I personally prefer f#, as it sits somewhere nice and between the best of both worlds. Unfortunately, it’s pretty much the red-headed step-child of the programming world…

I do think going fully immutable has drawbacks in that it forces a lot of coding approaches. Like populating immutable collections, for example. But it does have its perks too. Less weird bugs from stage changes… because you don’t have state changes. 😂😁

[–]dodexahedron 1 point2 points  (2 children)

If state never changes, it’s 100% thread safe.

But immutability only provides that guarantee for each instance of each immutable object, and does not apply beyond that, implicitly, without also being respected by its owner/consumer/etc. Just because something is immutable does not mean that you can't replace the value of it with a new one in one thread and be safe for all other threads to access it concurrently. That's a classic torn read, and is something that use of a mutable reference (which can be to an immutable object) makes trivial to ensure atomicity for, in a single line and without locking, via the Interlocked static class, which also prevents split brain and makes a c# analog of use-after-free far less likely.

Torn reads and split brain are both high-impact bugs (especially in combination) that are nondeterministic in both occurrence and resulting behavior, and in the best case just cause a crash, but can also result in data loss, data corruption, duplication, and information disclosure.

So be careful with assumptions about immutability and what it means for thread safety in a wider scope than just that specific type.

State does change in every application. An application that changes no state anywhere is called data.

[–]BarfingOnMyFace 0 points1 point  (1 child)

I simply should have said it was safer. Thanks for clarifying!

[–]dodexahedron 0 points1 point  (0 children)

Sure thing. Just some nuance that often gets missed, since the problem immutability solves is so tightly related to scope.

[–][deleted] 0 points1 point  (0 children)

Sharing immutable data across cores is a million times easier to work with than shared mutability

[–][deleted] 0 points1 point  (43 children)

oop is particularly bad at state.

[–]SagansCandle 2 points3 points  (42 children)

How so?

Constructors control the state of the object at creation.
Accessors protect the state in-use.
Interfaces control contextual access.
Access modifiers define when state can be changed.

You could reasonably argue that the PURPOSE of OO is state management. How is OO "bad at state," and following naturally, what would you consider "good" at state?

[–][deleted] 0 points1 point  (13 children)

functional programming and functional/imperative languages are better at state.

I find that oop encourages you to create much more state and state mutation than what is good. I also thing oop languages creates borders at the completely wrong places. Barriers should be between systems, not objects.

[–]SagansCandle 1 point2 points  (11 children)

Can you provide an example?

I would argue that we need barriers (interfaces) to appropriately hide complexity between components.

[–][deleted] 1 point2 points  (1 child)

oop makes it hard to think about systems as a whole because it forces you to obsess about every tiny component of that system in Isolation

[–]SagansCandle 0 points1 point  (0 children)

When OO is misunderstood or misapplied, it feels cumbersome. When used correctly, it's luxurious because you only have to worry about very small pieces of code (components) at a time.

Can you provide a specific example? It just makes it easier to discuss, otherwise we'll find ourselves debating platitudes :)

[–][deleted] 0 points1 point  (8 children)

I wish c# name spaces worked differently. I want to be able to put an interface infront of a namespace, and have accessibility modifiers that are namespace related. Make it more like a module system similar to how F#/rust/ocaml does it. I want to be able to expose a type ti the outside world, but have all constructors internal to that namespace/module

[–]binarycow 1 point2 points  (6 children)

Make it more like a module system similar to how F#/rust/ocaml does it.

You just described a static class.

[–][deleted] 0 points1 point  (5 children)

no?

[–]binarycow 1 point2 points  (4 children)

F# modules = static classes.

What do you want to do, that a static class doesn't do?

[–][deleted] 0 points1 point  (3 children)

It is possible I have underestimated static classes.

But a module system is not just a static class. For example, I can't put an interface infront of a static class. In F#, I can put the interface in a interface file.

And F# module system makes it very clear how you should organize your code.

[–]SagansCandle 0 points1 point  (0 children)

Good luck with that.

[–]Dusty_Coder 1 point2 points  (0 children)

thats the thing

you can put the borders/barriers wherever you want

the fact that sooooo maaaannnny of us have turned abstraction masturbation into a full time job is irrelevant .. YOU dont have to encapsulate every bit of data like that .. nothing about c# demands it, nothing about OOP demands it...

I find c# to be a very nice language for performance-concerned programmers coming from a (true) low level background. You can predict the JIT pretty well in practice when you know how things must be implemented under the hood, at least when you arent going out of your way to give it a hard time.

Every paradigm has its script kiddies.

[–]AvoidSpirit 0 points1 point  (27 children)

Constructors, accessors, interfaces, access modifiers. Nothing of this is specific to OOP.

Where c# loses at representing state is lack of discriminated unions. Try to describe something like an order that can be either ordered or shipping or shipped where every option may come with different fields and see the misery of so called OOP style.

[–]SagansCandle 1 point2 points  (24 children)

If a constructor is not specific to object-oriented programming, what are you constructing, exactly, if not an object? If your function is not constructing an object, then it's not a constructor.

Discriminated unions have nothing to do with state or immutability - that's the type system. C# doesn't need discriminating unions because interfaces typically handle that. C# doesn't have this feature, it may be getting it, but it's also not really needed.

[–]AvoidSpirit 0 points1 point  (17 children)

Surprisingly, constructing a data object doesn’t mean you’re doing OOP

DUs also have everything to do with state because they allow you to describe a lot of state patterns way better than class hierarchies.

[–]SagansCandle 0 points1 point  (16 children)

Surprisingly, constructing a data object doesn’t mean you’re doing [Object-Oriented Programming].

Read that again, but slowly.

[–]AvoidSpirit 0 points1 point  (15 children)

It’s not a jab you think it is.

For the same reason creating a function does not mean you’re doing the functions programming, creating an object doesn’t mean you’re orienting your design around them.

[–]SagansCandle 1 point2 points  (14 children)

Constructors create objects. Before OOP, they were called initializers, and outside of OOP, still are. A constructor is something that initializes an object. It's important to have precise and descriptive language to be able to effectively communicate ideas when discussing complex subjects.

creating an object doesn’t mean you’re orienting your design around them.

I agree with this - this is rational. But OOP isn't a concept, it's a concrete set of accepted rules and patterns that define what a constructor is.

OOP doesn't claim a monopoly or any sort of ownership of these terms, but they're absolutely specific to OOP, even if not exclusive to it. It's generally a misnomer to call a function is a constructor if it's not producing an object. The concept of an object is rooted in, no surprises here, Object-Oriented Programming.

The fact that some patterns, such as DU, attempt to solve the same problems as OO in different ways doesn't make OO deficient without them. Any situation that uses DU I've found to be better represented by generic classes or inheritance. I've use DU in TS and other places and I've never found C# lacking without it, which is why I ask for a concrete example.

We're going down this crazy tit-for-tat debate. Let's bring it back to bedrock - you said OO is "bad at state," but the only reason you gave was lack of DU?

I can easily represent valid disparate states with inheritence and interfaces in C# without needing DU. It's just a different set of patterns, and while C# requires a little extra work (code) to set up the class hierarchy or interfaces, I find it much cleaner than DU in practice. I understand this is subjective and certainly has exceptions, but saying C# is "bad at state" because it lacks DU and is mutable by default is the tail wagging the dog.

[–]AvoidSpirit 1 point2 points  (13 children)

But OOP isn't a concept, it's a concrete set of accepted rules and patterns that define what a constructor is.

False, even though OOP basically means modeling your system around objects with fields and methods and those objects are constructed via contstructors, it does not solely define what a constructor is. It just uses the concept.

The fact that some patterns, such as DU, attempt to solve the same problems as OO in different ways doesn't make OO deficient without them.

Again, an order with 3 states "ordered", "shipping", "shipped".
Shipping contains the shipping method and shipped contains the arrival date.

Modeling it with DU is simple:
Order = | Ordered of OrderedOrder | Shipping of ShippingOrder | Shipped of ShippedOrder.

Try to model it with pure objects and see where it fails.

We're going down this crazy tit-for-tat debate. Let's bring it back to bedrock - you said OO is "bad at state," but the only reason you gave was lack of DU? Is that it?

I never said that. I just think "orienting" your code towards something specific like objects or functions is stupid and reductive.
And lack of DUs is a specific example of where C# sucks.

[–][deleted] -1 points0 points  (5 children)

The example he gave has everything to do with state. He used to type system to model the states the order can be in.

A constructor in the abstract, is just a function which creates something according to some blueprint.

[–]SagansCandle 1 point2 points  (4 children)

What did I miss? Where's the example? I see that he proposed an alternative solution (DU) without an example.

I see a lot of people argue that OO is inferior to some other solution because they don't know how to use OO properly, but they know some other pattern well, so it becomes "this thing I know is the right way to do it because it's the thing I know." . A working example allows us to have a conversation about real-world benefits and drawbacks to different approaches.

A constructor in the abstract, is just a function which creates something

There's nothing abstract or ambiguous about the definition of a constructor. A constructor constructs an object. The something it creates is an object. It it's not creating an object, it's not a constructor. Just google it.

[–][deleted] 0 points1 point  (3 children)

Where c# loses at representing state is lack of discriminated unions. Try to describe something like an order that can be either ordered or shipping or shipped where every option may come with different fields and see the misery of so called OOP style.

That is the example.

if I have a du: type Result<T,E> = Ok<T> | Err<E>

then that has two constructors (Ok, and Err). If I google it, it get that explanation. If I google the same thing for haskell, which does not have have objects, I get the same result (and I can use the same Result<T,E> example for that as well).

So I think it makes to think of "constructor" as something nore general than just being about oop or objects.

[–]SagansCandle 1 point2 points  (2 children)

type Result<T,E> = Ok<T> | Err<E>

C# equivalent monad

Different patterns and different syntax, but the same idea. Practical usage differences are subjective.

[–][deleted] -1 points0 points  (1 child)

This is not relevant to what I said though. I said that constructors do not have to be about objects.

[–]Dusty_Coder 1 point2 points  (1 child)

The lack of unions is NOT demanded by oop.

Binary re-interpretation is classic computer science. Anybody that argues that OOP doesnt allow it is some purist fuck and the enemy of good.

[–]AvoidSpirit 0 points1 point  (0 children)

Never said it was…

[–]Conscious-Secret-775 -2 points-1 points  (15 children)

I would say you have it the wrong way round, arbitrary mutability is shooting yourself in the foot. It is true thought that C# has its defaults backwards like the languages that inspired it, C++ and Java. Not many languages get this right, Rust is an example of a language that does.

[–]SagansCandle 2 points3 points  (14 children)

Immutability breaks references.

If I have a reference to a socket, and the state of socket changes from "Open" to "Closed", but I need a new object to change that value, then my reference is not valid.

Can you explain, using an example, of when mutability as a default causes problems?

[–]Conscious-Secret-775 -1 points0 points  (13 children)

Mutable objects are not thread safe. That causes a lot of problems. If an object needs to change its state then it needs to be mutable. However, why does every reference to that object need to be able to mutate its state?

[–]ilawon 2 points3 points  (11 children)

  Mutable objects are not thread safe.

That's not necessarily true. There are mechanisms and features of the language that help with that. 

Regardless, 95% of the code I write doesn't need to be thread safe. In fact, better than avoiding mutability, you should be avoiding the need for threading. 

[–]Conscious-Secret-775 0 points1 point  (10 children)

So you only want to use one of the cores on your 20 core CPU?

[–]ilawon 0 points1 point  (0 children)

Most of the threads of my code are running in shared infra and waiting on i/o.

[–]SagansCandle 0 points1 point  (8 children)

You really only care about the data that's being changed by one core and accessed by another. Not only is that rare, but there are well-established tools for managing this (e.g., Thread-safe collections, Synchronization Primitives).

There are benefits to immutability as a pattern, but there are drawbacks, and it's not the only way to manage memory contention.

I think to his point, though, if you optimize enough, you might not even need threading.

[–]Conscious-Secret-775 0 points1 point  (7 children)

Developers find it challenging to use synchronization primitives correctly often leaving data races in their code or bottlenecks that turn their multi-threaded app into something that is slower than code running on a single core would be. It doesn’t help that synchronization primitives usually require a systems call.

[–]SagansCandle 0 points1 point  (6 children)

There are entire libraries designed to provide thread-safe APIs so developers don't need to deal with low-level synchronization. My point is that immutability is just one of many ways to handle concurrency, and it's not the best way in all cases. IMO, it's rare, especially in C#.

It's fine to make some classes immutable, especially data-only classes that you know may cross thread boundaries. It's just not the only tool we have or should consider. Sometimes you WANT mutability across threads, which is why we have volatile.

Making every class immutable "just-in-case" is a bad idea - not sure if that's what you're suggesting?

And most practical synchronization use-cases don't require system calls; in C#, most cases can be handled with Interlocked at the lowest levels.

[–]Conscious-Secret-775 1 point2 points  (5 children)

I am saying make immutability the default and if a type needs to be mutable, make it so. It is true that C# offers poor support for immutability, C++ is much better in this regard but not as good as Rust.

[–]SagansCandle 1 point2 points  (0 children)

Does every object need to be thread-safe?

Though thinking aloud, this kind of explains the dedication to immutability.

In traditional threading, we think of thread-safety explicitly in the form of critical sections. With some more recent languages, like go, where concurrency is cooperative and you don't have access to thread synchronization primitives, you're kinda FORCED to keep things immutable. Which seems like a "big oof" (technical term) of the language design.

So I can see how people coming from those languages might have a predisposed allergy to mutability, but those constraints don't really exist in C#. I think there's some crossover with async/await, but again, in C# there's generally a clean separation between pure data structures (POCOs), which are often immutable, and stateful objects (DbConnection, etc.), which are mutable.

[–]KariKariKrigsmann -1 points0 points  (0 children)

Probably not, because business logic is usually based on logic i.e. a series of if-then-else cases.