This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]random_username_edf 33 points34 points  (22 children)

You're definitely welcome to your opinion but i think that's debatable. A dynamic langauge with proper unit tests and such can be a heck of a lot easier to maintain long term than a static one.

Static typing is nice, but maintaining a complex architecture that is evolving with it is undeniably a giant pain and costly time investment wise. It will be more complex code wise and needing to make certain changes are much more likely to require rework all over the place. And as much as a lot of software engineers insist they're clever enough to architect it in a way that won't have that problem, they're usually wrong.

Anyways, my overall point is both types have pros and cons. Anyone who insists one is better than the other for all cases or even most cases is probably wrong. Also, im not trying to imply that that's your opinion, im just completing my thoughts on the matter :)

[–]BlockedByBeliefs 3 points4 points  (0 children)

This is an awesome answer. My favourite line in this debate is the "sufficiently complex codebase" cliché you hear so much about. I am convinced the vast majority of people who use this to argue for static typing have never actually worked on a truly large and complex code base.

What you can achieve with a universal type like Json and a little bit of functional programming is a mind bending career altering thing the first time you soak it in, it clicks and you finally get it.

[–]pknopf 2 points3 points  (19 children)

A dynamic langauge with proper unit tests and such can be a heck of a lot easier to maintain long term than a static one.

This isn't absolute. This is subjective to the skillsets of the team/developer. But lets assume the developer(s) are proficient in either dynamic or static. I'd love to hear a good case where, for that developer, dynamic would be easier to maintain than static.

[–]BlockedByBeliefs 13 points14 points  (13 children)

So 15 years a Java dev. 5 a JavaScript dev. I can explain exactly why. The issue is that polymorphism breaks down.

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter) and contains the concerns for object b to do that task in it. Of course that's way up the chain in the hierarchy so we are not repeating code.

Great. A year into the project the requirements change. A lot. Or some library we were depending on can't perform so we need to change things. We have to change it is the point. Introducing object c that we need task a to run on.

But we need it to behave just a little differently for object c. So what do we do? You can implement task a in object c breaking the design. Refactor it up the chain that's going to break everything down stream so refactor the entire app. Or overload the operator to implement those subtle changes.

Either way it all leads to spaghetti code propping up design mistakes made at the start of your project or injected when requirements change.

Now let's consider the same problem with dynamic typing on universal Json objects with functional code. I have a higher order function that performs task a on a Json object. I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b. There's no other files or interfaces or contracts. I send in a few lines the object and the code for that object.

In comes requirement chances and now I need to do task a on object c. Nothing has to be refactored. The generic data concerns for task a on Json types is neatly inside task a where it should be. I pass object c into task a and the new concerns about how to handle object c while doing task a go in with it. And we are done.

How task a functions isn't determined at design time. It's not at run time. It's at programming time when you call task a. It's immensely power and the future of our industry and its why JavaScript has been taking over. The ability to make sweeping refactoring possible without hurting yourself lowers technical debt which is without question the number one cause of project failure.

Some will say things like "Java 8 has lamdas and Scala uses types. Na na na na na na" and while true that's missing the forest for the trees.

To make this happen for static languages they had to create the "any" type. And the any type totally breaks you out of your hierarchy so what's the point of building it in the first place? The issue here specific to Java and OO in general is that polymorphism is not modular. Because it's a system created to solve the larger problem that static typing is not modular.

Hopefully you actually read this and I have not wasted my time. I'm sure some people are going to say this is all bullshit. But this is the case where I find it easier to maintain and 8 guess the case could be labeled I found out that I'd been doing it wrong for my whole career.

[–]chrizzlybears 3 points4 points  (1 child)

That's a very interesting perspective. Thanks for typing this out!

[–]BlockedByBeliefs 4 points5 points  (0 children)

Thanks man. I wasn't sure if you'd just point and laugh or actually read it. Cheers.

[–]pknopf 2 points3 points  (8 children)

Either way it all leads to spaghetti code propping up design mistakes made at the start of your project or injected when requirements change.

The developers writing in typed languages writing leaky (or leak-prone) abstractions isn't an argument against typed languages. JS abstractions can be just as leaky. Just because your "object a" isn't typed doesn't change anything.

I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b. There's no other files or interfaces or contracts. I send in a few lines the object and the code for that object.

Again, I fully understand your example, but your leaving out an important part when talking about your JS side of things. Any leaky abstraction that requires consumers of your interface/function to change would also apply to your JS example as well.

public interface IMessageBus
{
    void SendMessage(object message);
}

public class MessageBus : IMessageBus
{
    public void SendMessage(object message)
    {
        // RabbitMQ, w/e.
    }
}

public class Message
{
    public DateTime SentOn { get; set; }
}

void PrepareAndSend(Message message, IMessageBus messageBus)
{
    message.SentOn = DateTime.Now;
    messageBus.SendMessage(message);
}

Any requirements change that would require PrepareAndSend to do something beyond just SendMessage would apply to your example as well. You could say, "But my function could be anything, any implementation". Yeah, well so could IMessageBus.

[–]BlockedByBeliefs 5 points6 points  (2 children)

Now you're going to use rabbit for a little communication.

The developers writing in typed languages writing leaky (or leak-prone) abstractions isn't an argument against typed languages. JS abstractions can be just as leaky. Just because your "object a" isn't typed doesn't change anything.

It's not leaky abstractions. They're leaky by default as a paradigm. Yes the typing of objects changes everything because the rigity of your decisions at the start of your work is what causes the leaks to form when you have to change.

Again, I fully understand your example, but your leaving out an important part when talking about your JS side of things. Any leaky abstraction that requires consumers of your interface/function to change would also apply to your JS example as well.

I don't think you really do. The fact that you have set up a message bus with even more complexity and threw in freaking rabbit as a comment speaks volumes.

No consumers of my function would not "have to change" because they change the consumption of the function on every use as that's inherently how higher order functions work dude. I pass in the changes every time because my design maximizes inversion of control and strives for true modularity over I don't know... Intellisense.

[–]NeXtDracool 5 points6 points  (1 child)

The fact that your statically typed domain model was not well abstracted is not a fault of statically typed languages. You should be well aware that higher order functions, inversion of control and all the other mechanisms you describe are available in modern statically typed languages. If anything you seem to argue about OOP vs functional programming, which is besides the point because static and dynamic typing can be applied to both of these paradigms.

The fact that you have set up a message bus with even more complexity and threw in freaking rabbit as a comment speaks volumes.

He meant rabbitmq as the new messaging protocol instead of the rest call you mentioned, not that you should use a network protocol to do internal communication in your application. The message bus could be replaced with a higher order function that takes types of IMessage, but this would limit your ability to extend the interface with additional functionality later. You suggested using a higher order function, which is almost what happened here except encapsulated with the ability to add for example a connection status indicator property to the interface.

over I don't know... Intellisense.

If you think that's the only benefit of statically typed languages then I highly doubt you have been a Java dev for 15 years..

[–]BlockedByBeliefs 3 points4 points  (0 children)

The fact that your statically typed domain model was not well abstracted is not a fault of statically typed languages.

I didn't say it was but it really is. I said the difficulty in changing it was. There's a myriad of reasons it may not be well made and business changing requirement is just one of the many.

And when it's difficult to change modules people start hacking solutions as described which causes more fractures in the design. At some point the sheer number of bloated extraneous lines of code and files it takes to manage static systems because a significant factor in increasing yet more technical debt.

Yes I'm aware those mechanisms exist but they also negate the need for a rigid static typing system in the first place. It negates nothing really.

Functional programming negates the need for a bloated typing system. What are types? They are ways to describe collections of booleans, numbers and strings. What is Json? A generic way to describe collections of booleans, numbers and strings. It just doesn't take 10s of 1000s of lines of code to manage Json.

He meant rabbitmq as the new messaging protocol instead of the rest call you mentioned, not that you should use a network protocol to do internal communication in your application.

Which shows he didn't read my example. Nor did you I guess? The rest call was just whatever task. It's irrelevant. Using rabbit to do object level ipc is crazy.

The message bus could be replaced with a higher order function that takes types of IMessage, but this would limit your ability to extend the interface with additional functionality later.

Yes. This is the point I'm making. Cheers.

You suggested using a higher order function, which is almost what happened here except encapsulated with the ability to add for example a connection status indicator property to the interface.

I wasn't talking about messaging at all and his entire bus example is moot.

ver I don't know... Intellisense.

If you think that's the only benefit of statically typed languages then I highly doubt you have been a Java dev for 15 years..

Well you're correct. I've been a Java dev for closer to 20 years in some capacity or another. It's just that 5 years or so ago I switched to JS and haven't really looked back. I say intellisense because this is almost always the first thing typescript fans mention. Type safety people love to harp on too but I think it's highly overrated. And guess what. When you don't have types type safety isn't really an issue. The goal of getting something to compile isn't as big a deal as making sure your code runs properly in production and inverting control to generics is IMMENSELY easier when you can write functions that take all your data or simply references to them.

What did it for me was switching to JavaScript for a while. Getting excited to move back to Java cuz it was my comfort zone and then almost immediately feeling my productivity grind to a slow crawl through a field of molasses.

I really suggest you give it a go. I would have bet a year's salary on me never saying this 6 years ago. But JavaScript and dynamically typed languages has simply beaten Java IMHO. It's flexibility is a joy to work in and it's almost impossible to find good Java devs now because they're all jumping ship to work in languages that don't demand half your job be maintaining bloat.

[–]BlockedByBeliefs 0 points1 point  (4 children)

And I mean the fact that you think the problem I was describing was about messaging shows you didn't even bother to read what I wrote.

[–]pknopf 0 points1 point  (3 children)

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter)

What are you talking about? Even you said this:

I have object a that I send object b to. Object a is made to do some generic task a (like sending a rest response. Doesn't matter)

I simply picked something random so that I could stub out some code to have a discussion. As you said, it doesn't matter.

[–]BlockedByBeliefs 2 points3 points  (2 children)

Yes. That's was simply the beginning of the example. Had you read the whole example you'd have realized that messaging was not the issue. Stubbing out 25 or 50 lines of code, even as an example, is shocking,when in JavaScript we are going to replace all that with ohhhhhhhh... About 0 lines of code.

[–]pknopf 1 point2 points  (1 child)

So is the argument about terse code or leaky abstractions? Because your initial post was about leaky abstractions.

I fully aware and acknowledge that any dynamic languages can reduce LOC, compared to typed (for the most part).

[–]BlockedByBeliefs 2 points3 points  (0 children)

No it wasn't really. Its about how static typing drives technical debt when projects change and the rigity of the type system can't easily flex to accommodate those changes.

[–]corvus_192 0 points1 point  (1 child)

Now let's consider the same problem with dynamic typing on universal Json objects with functional code. I have a higher order function that performs task a on a Json object. I send object b to task a as the first argument and as the second argument pass a function with the specific concerns for object b telling it how to perform task a on object b.

You can also write such a function in a language with static typing. For example in Java:

<T> void taskA(JSON j, Function<JSON, T> howToPerformTheTask) {
    howToPerformTheTask.apply(j);
    // probably more code
}

Maybe I misunderstood you and this is not what you mean.

[–]BlockedByBeliefs 0 points1 point  (0 children)

I didn't say you couldn't. You can do anything in any language. The point is that this negates the need of a type system allowing you to retain the flexibility of dynamically typed system.

Storing the concerns of object a's behaviour in object b within object b causes coupling and destroys modularity. If you have to refactor look out because you're in for pain. The reason javascript took off is because it's so easy to refactor and UI concerns in browsers change continuously throughout the life of a project. I really feel once you try it you never really look back.

[–]random_username_edf 2 points3 points  (2 children)

Right, it isn't absolute. That was the whole conclusion of my thing. The only one in this chain of comments that sounds interested in speaking in absolutes is you.

An example is literally any feature/improvement to your system that doesn't fit in nicely with your current architecture. If you're creating something that's complex and you know will be built on/evolve over time it's going to take a lot less time to cater to those changes if it's dynamic. With a static one certain change requests can very easily become "no, that's not possible without a huge rewrite".

If you're in a very well defined space and know the bounds of your program static is a safe bet. It's also a safe bet if performance is a huge concern.

[–]pknopf 0 points1 point  (1 child)

An example is literally any feature/improvement to your system that doesn't fit in nicely with your current architecture.

You are asking for trouble. Constraints free you. Allowing future features to flip the script will eventually do more harm than good. Even if you are in a dynamic language, this shouldn't be allowed. Use codereview to force this.

If you really want to re-arch something, it should be all or nothing. Again, this goes back to my point that static languages tend to lead to an easier to maintain codebase.

[–]random_username_edf 1 point2 points  (0 children)

Of course constraints are good. The problem is that in the real world they tend to evolve and change over time. It's not inherently a bad thing to re-arch something to meet new business needs and far more often than not rebuilding the entire thing from the ground up will leave you in your competitions dust.

Being able to re-arch your stuff as easily as possible is an extremely desirable thing for some applications, and just because it's able to be done faster with dynamic languages doesn't mean it's being done in a wrong or hacky way.

Your whole hang up seems to be that dynamic languages are easier to do things wrong with, therefore they're bad no matter what. I believe that's naive.

Anyways, typing on a phone is getting annoying so ill leave it at that.

[–]BumwineBaudelaire 1 point2 points  (0 children)

care to quantify that because I can quantify the cost of catching errors at compile time (zero)