top | item 5020486

(no title)

darrencauthon | 13 years ago

So you'd say that this code is better:

    post.publish Date.now
than

    post.publish
When the publishing will only happen now?

I'm a Ruby developer who disagrees with DHH strongly on this issue, as well as his stance on SOLID and testing. I also have years of experience in C#, using TDD and using dependency injection and IoC containers everywhere. However, I think there's a point to be pulled from DHH's post about unnecessary ceremony around dependency inversion (the more important thing) and static languages.

If I were to write this "publish" method in C#, I couldn't even put it in the Post object. It all looks fine and dandy now, but what happens when the first wave of change comes in? Say, if we were to have to stamp an "updated_by" field? Then we get to this:

    def publish(time, updated_by)
       self.update publish_at: time, updated_by: updated_by
    end
And so on, and so on. The dependencies have to be passed to the object, so every dependency flows up to become dependency on any object that uses it. And since having method calls like ".publish(Date.now, CurrentUser.name)" everywhere really stinks for readability and DRY, and since I can't make Date.now a constructor requirement to create a Post, I always found myself having to do this (in C#):

    public class PostPublisher
        private IDate date;
        private ICurrentUser;
        public PostPublisher(IDate date, ICurrentUser){
            this.date = date;
            this.currentUser = currentUser;
        }

        public void Publish(post){
           post.updated_by = currentUser.name;
           post.published_on = date.now();
           // save????
        }
    }
... and let the IoC container wire this sort of stuff up. Dependencies tied to this operation are isolated (as well as every other operation). But UGH, is this any better? So many classes and LOC just to do simple things.

I just don't see a good solution to this, and I'm not satisfied writing "good" code that handling is a pain. I decided to try Ruby as it seemed to have answers for this, and it does.

You can call the ability to use the Date object and still change it a "global variable" or "linguistic flexibility," but the truth is: In the applications we write, published_at is only going to be set to Date.now and updated_by to the currently logged in user. And as soon as we write the application and deploy, it will serve the business needs wonderfully. As a C# developer, I justified the costs of doing these end-arounds as necessary to maintain a TDD'd codebase that could easily be changed later. As a programmer, though, I can't justify the costs of writing dozens of lines of code when I can TDD one line of code. And I sure as heck don't want to eat the costs myself. So today, I just do it with one line of code really fast and make the client happy.

discuss

order

kevingadd|13 years ago

'the publishing will only happen now' is the kind of assumption that ends up being false very rapidly. Or immediately, if you're writing automated tests. That's kind of the point.

I tend to write stuff similar to your example in C# (though I tend to make them 'public readonly' instead, because I hate accessors), and I agree that it's kind of tiresome. But that is really just a deficiency in C# - it lacks a concise way to express such patterns.

And to be fair, it's absolutely true that if you wanted you could just use global variables. You are absolutely paying an up-front cost when you do this, but you should look at that cost as an investment: In the long run it will pay off by making your code easier to understand, easier to maintain, and easier to extend, because its dependencies on global state are painfully obvious. Sometimes that investment may not be worth it, so you have to make the judgement call.

h2s|13 years ago

    > 'the publishing will only happen now' is the kind
    > of assumption that ends up being false very rapidly.
I think you're right, and part of the problem here is the time example clouding people's judgement. The fact that we as humans experience time as a continuous global phenomenon is irrelevant from a software engineering perspective. The current time is data no different from any other data.

It's not even difficult to think of an example of your point about the assumption quickly becoming false. Presumably our "publish" method exists within a blogging application. An extremely common feature in blogging software is some tool for importing posts from other blogging applications. In that situation, you need to be able to pass in the time as input.

darrencauthon|13 years ago

I write automated tests (using TDD), and it's not an assumption that would end up in my tests. Testing would not force me to introduce the date as an argument, at least not in Ruby.

That's the thing about Ruby, or perhaps another true thing that can be gathered from DHH's post: Ruby lets you make the judgment call you're talking about, without sacrificing the testing. C# would.