Hacker News new | past | comments | ask | show | jobs | submit login
Ruby Best Practices (full book available for download) (rubybestpractices.com)
151 points by figured on March 17, 2010 | hide | past | favorite | 55 comments



For the tl;dr crowd, this will take you straight to the PDF:

http://sandal.github.com/rbp-book/pdfs/rbp_1-0.pdf

(But please read the article, it provides context :)


you provided a tl;dr link to a BOOK???! haha, wednesday morning irony ftw


Hah, the irony here did not escape me. But good point.

So... here's the tl;dr; for the book itself:

"Ruby provides many ways to do things, many of them decent. But some are usually better than others, depending on the context."


I recommend this book whole heartily. I have been reading the chapters as they have been released, an excellent resource.


So, is this the full version of the O'Reilly book [1] or something different? I'm curious about the business end of this project: did you sell this PDF before giving it away for free? How much revenue have you brought in? How much time have you spent writing/compiling/editing this book in it's current state. It seems like a very highly regarded book (judging from the Amazon reviews) so perhaps I am just out of the loop on this one, but I find the business aspect of these ventures most interesting.

[1]http://www.amazon.com/s?ie=UTF8&tag=mozilla-20&index...


It is exactly what was used to generate the print book, just with some web enabled features like links. And yes, this is the PDF you'd get if you buy it from O'Reilly.

Marketing-wise, it was a gamble, because the open source release date wasn't contingent on sales or anything like that. We launched the book and gave a 9 month exclusive rights period to O'Reilly.

This at least takes into account the fact that most books sell the majority of their copies in the first month or two, but I didn't have like a minimum sales number before I could open source the book or anything like that. In fact, the print book says right in the front cover that it'd be open sourced in March.

The book did okay sales wise, not great, but not horrible. It definitely did worse than it should given that (at least from the looks of it) people really, really like it. But this isn't really up to the readers, it's up to the distributor. Basically, much of our sales come from wholesale, and with paper book stores on the decline, and the saturation of the market with Ruby books over the last couple years, it was hard to convince them to pick up a lot of copies of the book.

As of right now, I am just about at the point of clearing my advance. I will share specific numbers at some point, but let's just say that the advance probably works out to minimum wage or less when compared to the amount of effort put in.

Interestingly enough, the gradual week by week release of chapters really led to a nice spike in sales, coming from what was essentially a steady decline except for a spike around the holidays. So actually, I think open sourcing the book was good for sales in that regard. Since it might be hard to find the book on the shelves in a book store, this gives people a chance to "try before they buy".

Unfortunately, because my book was selling above average as an ebook, I've probably effectively killed those sales today. Then again, maybe people will still buy the Kindle and Iphone editions, who knows.

At the end of the day, I think neither O'Reilly nor myself expected this book to be a big bread winner. I love the book because it embodies the most interesting things I've learned from the smartest people I know, and I think that comes through in the writing. I wrote the book because the idea popped into my head fully formed and demanded to be put out on paper. So that was my biggest win.

But from an authors perspective, RBP has given me a great boost in exposure. I already had name recognition in the inner circles of Ruby community, but after writing the book, I find more opportunities from folks I probably wouldn't have been able to reach otherwise. This has been good for my open source projects, and good for my work.

I'm glad I did this, and it worked out well. I'd recommend it to others, for sure. Of course, buying the book will make O'Reilly happy, so go ahead and do that if you want :)


It's quite a risk to open up a book like this. More so if the main sales are expected to be E-versions.

I've talked to a few people who did things along this line, and they are of two minds. One camp says it kills sales. The other says it boosts sales, but in the latter case I don't think there was a PDF version even offered, and they made the book available as a Web site.

I encourage people who think this book is valuable to pay for it in some form, to encourage publishers to do more of the same, and to reward the author.


BTW, people should Google around for Gregory Brown's conference videos to hear him talk about, and demonstrate, assorted instances of good and bad code.


Thank you for the details! Much appreciated.

Even though you may not have been paid a "fair wage" for the work that you put into this book, it sounds like you are very happy with the outcome and the experience and the future benefits that you will undoubtedly reap from this project.

Congratulations and thanks again!


Actually, I don't think my advance was unfair. I self published a book a couple years ago and it was a logistical nightmare. When you think about what a publisher actually does, it's really a daunting task.

I think there was a post here on HN about that recently, but I can't remember the link. The author who wrote it described pretty much the exact process I went through, even though I think he was working with another publisher...


Could you explain a little bit more about the "logistical nightmare" aspect of self-publishing? I'm thinking about self-publishing a book of my own, so I'd love to hear more about the obstacles you came across and how publishers make it easier.


Well, I think that it can be profitable, especially if going EBook only. There is some great advice on it here:

http://macournoyer.com/blog/2010/03/01/promote-cyopl/

But for me, I didn't want to spend a ton of time and effort on marketing, and my topic was a niche within a niche at the time (I wrote the Ruport Book with Mike Milner -- ruportbook.com).

After a year or so, instead of being thousands in the black, we were hundreds in the red, even though the book sold a few hundred copies. So we just made it available at cost and closed the doors at that point.

This may be because the HTML version was available for free when we started. It may be because we set price points very low (I think our PDF was like $8), it may be because I started an LLC and wasted tons of money on absurd taxes just for keeping the doors open. It was a bunch of epic fail on the business side of things, and that at least taught me a lesson.

I talked to O'Reilly and though they were very inflexible about certain things (due dates, typesetting, etc), they were open to change pretty much everything that mattered to me, and did. As a result, my book shipped early and at a much higher quality level than I could imagine doing myself.

Of course, I could tell you stories about other publishers I either pitched ideas for, did tech editing with, or wrote chapters for that'd make your head spin. So I wouldn't say that professional publishers are good across the board, just O'Reilly pretty much let me run with my ideas and ended up being very accomodating.

This may have something to do with the fact that I blogged and wrote articles with them for a while before writing a book, and because my editor is also a friend of mine from the Ruby community. But I know folks who have pitched O'Reilly cold and had similarly positive experiences, so I don't think that's the case.

Ultimately, publishing is going to be what you make of it. Self-publishing gives you full control, but it also gives you full responsibility. I think you can make money on it if you market effectively, and I think it can be a worthwhile if you really want to do something unique.

But for me, someone who just wanted to get some ideas out there and maybe make a buck or two in the process, doing all of the work of self publishing really was horrible.


"fair wage" was a poor choice of words on my part. I didn't mean to imply that your payment was "unfair", but rather that minimum wage is to low (imo) for an advanced programming book.


Writing tech books is something of a crap shoot.

I had a mixed blessing with my first book effort, Pro VB 6 & XML. I started out as one of about 10 authors, expected to contribute one or two chapters, and ended up as one of two authors and writing a little over half the book.

I got to write about stuff I was already quite familiar with, so it went pretty quick. And the book did OK, so I made some decent money, and the time spent was well worth it.

However: It gave me the impression this was a repeatable event. :)

Aside from the odd chapter for Wrox (most of which never saw the light of day because books got canceled, though I still got paid since I was not on royalties for them), my next book effort was Beginning Ruby for Wrox, around 2001 or so.

Way more work, the tech market was turning to shit, and Ruby was still an uber-niche area. Wrox then went bankrupt, and I never saw my final check for work completed. I got, I think, a grand for several months work.

Just before Wrox burned I had some discussions with my editor who suggested various ways to monetize what I had already written. Mostly it was "have a Web site".

Thing is, there is so much free quality info already available that drumming up eyeballs and getting people to part with cash is quite tricky.

If you're thinking of writing a book you really need to have your eyes open and keep realistic expectations, and be comfortable with what you can expect. If the goal is to build a rep, then sales may not matter. But you are still competing for attention.

Greg has the Ruby Best Practices blog; I'm quite honored to be allowed to publish there (and I wish I could get my ass in gear to write more). I'd be all for having ads on the site and finding a way to get some cash back to Greg & O'Reilly so long as it didn't spoil the reader's experience.

But I think that's quite doable.

I also think E-books can do better by exploiting their E-nature. I've had some ideas for E-books that weren't simply paper books turned PDF. There is lot's you can do with them, and perhaps there are ways to hook them into a recurring revenue service (say, a subscription to Best Practices screencasts or something.)

I can't emphasize enough, though, that people who find these things valuable should see about remunerating the author. If you want to see more things like this, help out.

People like Greg deserve a lot of credit for trying things out and trying to provide quality material in an affordable way. He is one of the reasons I'm proud to be part of the Ruby community.


Hey Gregory, thanks a lot for sharing your experience and of course for the book itself. I'm just beginning to write an R graphs cookbook. Do you have any tips for a first time author?


Like most authors, I'll encourage you to think about not doing it, first :)

If you're just looking to get your ideas across, you'll almost certainly get more mileage out of writing articles, doing talks, etc.

And if you're looking to make money, unless you get very lucky, you won't make it directly off your book. It will almost certainly lead you towards other opportunities, but really all it creates is potential, things won't fall into your lap.

Still not discouraged? Then you might really, really want to write a book. That alone is what got me through the two I wrote. So then, go for it!

Here are some rough tips:

0) Get to know publishers a bit before submitting a proposal. I've had friends who got their book accepted cold, but really, there's little to differentiate you from the giant stack of proposals publishers receive daily if you come in without them knowing who you are first. This will also help you quickly identify the sort of people you don't want to work with. :)

1) Have a vision for your book. Get a sense of what you want it to be before you start writing it. This can be something very flexible, and doesn't need to button down any specifics, but know what the structure will be and test it out by attempting to write a chapter against it. If that doesn't work, reiterate. If you are working with a publisher that enforces a particular style, make sure you love it. But avoid those sort of restrictions if at all possible. I've written chapters on books where I was forced to write in a way I didn't like, and it was terrible.

2) Get together a good advisory group. Find the people you look up to in your topic area and ask them to discuss your chapters as you write them. We had a private mailing list for this in RBP, and it was amazingly effective. While tech reviewers can turn up a lot in isolation, you uncover the core issues when you ask them to discuss things as a group.

3) Expect your topics to change somewhat. If you find a chapter extremely hard to write, but there is a suitable replacement, consider introducing it. Completeness is a virtue, but I rather leave something out than cover it poorly. Some chapters will be hard due to necessary complexity, and that's okay. But there are others that are hard just because there is no reasonable way to write about them given your resources. Avoid those.

4) Write steadily, and try to make sure you're producing new content every week. Don't obsess over individual chapters, keep the whole of the book in mind. Leave yourself time to go back and breathe life into your more anemic content, but be sure to keep on rolling, even if its against your better judgment.

5) When forming a chapter, write out a bunch of code and examples first. When you think you have a cohesive set, sit down and write as if you're explaining the code you wrote to a friend or co-worker. Make sure your examples can flow together nicely, where possible. I found this approach to work well with RBP. If I couldn't come up with a great set of examples that went well together, I went back to the drawing board without ever writing a line of prose.

6) Ignore all of the above, and find a flow that works for you.

This stuff is hard, it's okay for it to be hard, and really, all you need to keep in mind is pretty much every other published author has been through the same hell that you'll find yourself in.

But you get a big reward in the end. You (eventually) get your sanity back.


Wow! I'm glad I came back into this thread -- these are all such great tips. Especially the advisory group (item 2)


The point about leaving stuff out is very important.

When I was co-authoring Pro VB 6 & XML I was always coming across topics that could be books in themselves. Database details, designing COM objects, stuff like that.

I hated the idea that a chapter would leave the reader n limbo over some key aspect, but also didn't want to book to be even larger than it was likely to be, and explaining stuff can get hard.

So there were lots of places where I simply had to point and refer the reader to some other book or resource. There's not much you can do about that other than try to be mindful of when it is the right choice.


I decided ahead of time that my book would be exactly 300 pages. I submitted an exactly 300 page manuscript, but I think my copyeditor shaved two pages of extraneous words :-/

Anyway, point being, I was pretty obsessive about not letting the book balloon. It forced me to put the knife to a lot of good content that wasn't quite great.


Setting a max page count is a good plan. I'm sure that had never occurred to anyone at Wrox, where books were routinely at the 1K+ pages mark.


Thanks very much. Those sound very good. I've bookmarked this thread so I can come back to it.


Keyword arguments via hashes is certainly not a best practice for "Beautiful API Design". Sending messages is part and parcel to good OO design. Code that deconstructs hashes to emulate keyword arguments is invariably hideously complicated.


I agree with you from a language design standpoint, but seriously, I doubt you have much Ruby experience if you're saying this. And if that's the case, you really don't have a basis to be commenting on my book in such a critical and dismissive way.

Using positional arguments for more than two or so things creates connascence of position, which quickly becomes annoying.

Using block based DSLs so that you can break things down into small functions that each do their part is a good way around this, but these tend to be hard to extend dynamically.

Typically well designed Ruby systems provide both: Pseudo keyword arguments for dynamic needs, and some DSL-type syntax for pleasing the eyes.

Yes, it sucks that we don't have real keyword arguments. But no, it's not nearly as bad as you think.


I've been using Ruby since 2005 and I am a top 100 contributor to Rails (http://contributors.rubyonrails.org/). Rails design suffers for its heavy use of "keyword arguments".

positional arguments for more than two or so things creates connascence of position

Which is precisely the reason that you shouldn't pass more than two arguments to a method (unless you are creating a list or hash for their intended purpose). If you have more than two arguments to a method, it's a sign that you should reify them into an object.

Using block based DSLs so that you can break things down into small functions that each do their part is a good way around this

Why do people love making things complicated by misusing blocks. Just create an object and set slots on it. Blocks are best used for delaying computation, not setting up state.

I think that Zed Shaw's essay on the "Master" and the "Expert" is applicable here: http://www.zedshaw.com/essays/master_and_expert.html


Wow, and this is exactly the kind of douchebaggery I was trying to get away from in my book. You know, I think on the first page I mention that the answer to "what the best way" to do something in Ruby is always "it depends". The notion that you can follow singular design principles in all situations is absurd, if you ask me.

Context is king, and what you've done is taken a single point from my book, ripped out its context, strawmanned it to death, and used it to bolster you're own "Top 100 rails" status.

I encourage readers to actually see what I had to say in the book by downloading the PDF before taking these arguments too seriously. If there are some constructive examples to be shown that seem promising and pass peer review, they'll definitely make it into the open source version.

It might even be worth it to write up a section describing the tradeoffs that richcollins has pointed out, but it's definitely not something as universal as he makes it sound.


used it to bolster you're own "Top 100 rails" status.

I've mostly stopped using Rails and Ruby in favor of Io, so I have no interest in bolstering my Rails status. I was just providing evidence to counter your assertion that I was a Ruby newb.

The notion that you can follow singular design principles in all situations is absurd, if you ask me.

I agree that "it depends", but using Hashes as arguments has become idiomatic Ruby, which is unfortunate in most cases.


I wasn't assuming you were a newb, just that you didn't have much exposure to what are commonly regarded as well designed Ruby libraries. It didn't occur to me that you'd have had that exposure and yet sound as if you were shocked and appalled about what is common practice.

I still would really like to see examples of well designed open source Ruby projects that follow what you consider to be decent design principles. I think that'd make a much stronger case for your argument, and might open my eyes to something that's been in a blind spot.

Personally, I feel like a certain amount of API design is bound to what consumers will expect. We can certainly stretch and shift their tendencies, but if we fly in the face of them, our perfection in a vacuum will never be appreciated.


Which libraries do you consider well designed? I'll let you know how I think that they could be improved.


I've already written an entire book on this and given it away for free. I suggest you read it and write a review. Be specific, and show good counterexamples. I'll be happy to link you when you do.

Until then, stop acting like you're some sort of authority (NOTE: This is not to claim I am -- because I'm not and no one is.) I just think it'd make sense to put your ideas into a bit of context so people can see that you're not just being critical for the sake of being critical.


    #from your book
    
    def distance(*points)
      case(points.length)
      when 2
        x1,y1,x2,y2 = points.flatten
      when 4
        x1,y1,x2,y2 = points
      else
        raise ArgumentError, "Points may be specified as [x1,y1], [x2,y2] or x1,y1,x2,y2"
      end
      Math.hypot(x2 - x1, y2 - y1)
    end

    puts distance(1, 2, 5, 6)
    puts distance([1, 2], [5, 6])

    # why would you choose to make a complicated interface that requires a complicated implementation?

    # simple, clear interfaces that allow for simple implementations
    # are almost always preferrable to complicated ones that save a
    # keystrokes through syntax

    class Point
      attr_accessor :x
      attr_accessor :y
  
      def initialize
        self.x = 0
        self.y = 0
      end
  
      def self.with(x, y)
        new.set(x, y)
      end
  
      def set(x, y)
        self.x = x
        self.y = y
        self
      end
  
      def distance_to(a_point)
        Math.hypot(a_point.x - x, a_point.y - y)
      end
    end

    puts Point.with(1, 2).distance_to(Point.with(5, 6))


So you mean that you prefer this:

Point.with(1, 2).distance_to(Point.with(5, 6))

Over this?

distance [1,2], [5,6]

I guess it depends on how many interesting things you wanted to do with points. As things get more complicated, maybe something like this is worthwhile, but I don't feel that making everything an explicit object is categorically better.

But the section you're quoting is among the most contrived examples that RBP gives, it's mainly just part of a list of the different sorts of argument processing Ruby offers and the trade-offs betweeen them.

Maybe you skimmed and missed that, or maybe you read through and chose to ignore that detail.


The problems I have with "distance [1,2], [5,6]" are:

0. It breaks the fundamental OO convention and doesn't encapsulate logic and data.

1. It's not clear what it means unless you look up the docs.

2. It's implementation is complex, making it more difficult to understand and maintain

3. It fails to separate concerns, making it less extensible/modifiable. e.g. What if you wanted to override the setting behavior? You couldn't do it in one place.


Would you prefer a *rest argument? An options hash is the best way to make your ruby API flexible.


Plain old objects are the best way to make your API design flexible. You can't easily change the behavior of setting a key/value pair for an individual hash. Objects are designed to implement behavior, Hashs are meant to hold key value pairs.


Right, the keys/values need to have supporting logic. And yeah if you're sticking procs in a hash, just use an object; but if your object is just a bunch of fields, use a hash. And often that's all you need. I just want to override the marginWidth on this fooBar.

Remember you don't need to destructure the hash. You've got a local one initialized to sensible defaults, and you .merge! it with the param one, and then just use the hash values as your local vars.


If your object is a bunch of fields, you should still use an object. It might need behavior later.

I think the central issue is that there is too much friction to creating new classes in Ruby. It feels official and final. In Io, you just clone an existing object like you would send any other message.


There's also an embrace of YAGNI and "Do the simplest thing possible ..."

I'd rather start with a hash and then see if it later demands more, than start inventing objects that, based on existing use cases, need only be hashes or structs.

I'd also rather go through a number of pre-1.0 iterations to get real-world usage to shake out the painful API parts. Up until then I'm happy to break compatibility if experience says there's some bad code in there.

Monkeybars, for example, had some methods that began life using positional args, but they were changed to use hash values. I hated it.

Aside from the extra typing required for every call, with positional args I got used to reading code and seeing things in a fixed order, so when I had to use the method myself I knew exactly what needed to go where. (These methods had 2 arguments, with maybe an optional third or so.)

With the hash args the order could vary so I had to keep looking up what the possible argument key values could be.

(Thing about using hashes for args: with positional arguments you need only know what role an argument is playing in order to know what sort of value to pass in, but with a hash you have to know the exact key name. That ends up being more stuff to look up in the docs, especially if the API designer has a quirky way of naming things.)

You might think that at least for a code reader the use of hash keys would better document the call, but the method name and the names of items being passed in were almost always sufficient.

I suspect this particular change was a result of over-thinking these methods and expected usage instead of actually paying attention to what the code was doing right now, and how people were using it right now.

There are times when my gut tells me, "You know what's going to happen with this code later", and I try to take that into consideration, but (usually) simple wins for me.


With the hash args the order could vary so I had to keep looking up what the possible argument key values could be.

You can set slots on objects in any order that you want to:

    Foo.new.set_x(x).set_z(z) ...


That is insanely ugly. I will pass, thanks.


Setters are composable and you can modify their behavior. Hash literals ... not so much.

It would seem you only see the surface beauty of things. [redacted -- good point]


How about adding this to the Point class?

  def to_hash
    {:x => @x, :y => @y}
  end
  
  def to_ary
    [@x,@y]
  end

  def [] i
    case i
    when 0,:x
      @x
    when 1,:y
      @y
    end
  end
Should work if the receiving method interprets the arguments properly.


"It would seem you only see the surface beauty of things."

Is that how you defend an API? Insult the user?

Holy shit that's stupid.


Can you recommend an Io book for me? I want to hunt down the author and try to encourage them to make their code more Rubyish. Because you know, that's helpful.


I did recommend to the Io author that he create a Hash literal syntax because I like the fact that Ruby has one ;-)

To be clear, I think that it is great that you created this book. I just responded as I did because I'm often frustrated when I use Ruby APIs that sacrifice good design (imo) for syntactic terseness.


The book is opensource, why not submit a <strike>patch</strike> pull request. :)


In all seriousness, all it takes is some code examples that make me and a few others say "whoa, cool" and it'll make its way in.

If RBP becomes "Gregory Brown's Best Practices", it'll be epic fail. I am encouraging folks to prove whatever they can wrong in this book.


The sandal username on GitHub sounded familiar, so I checked and realised its the same sandal that does the Prawn PDF library: http://github.com/sandal/prawn

That gives the book some extra credibility in my eyes.

EDIT: OK, now that I've read the foreward, I see Matz himself mentions Gregory's work on Prawn. Doh.


Yes, I started Prawn via the "Ruby Mendicant" project: http://majesticseacreature.com/mendicant/

I'm still active in the project as a maintainer. But really, these days you have Brad Ediger, James Healy, and Daniel Nelson, along with about 60+ contributors to thank for where Prawn is heading.

My job is mostly to sit around naming things, and warning people about the fragility of the early code I wrote. But we will have a 1.0 plan soon, and no matter who does the work, that'll be good for everybody.


This looks sweet. Thanks sandal (for writing)/figured (for posting).


Thank you for writing this, and thanks for open sourcing it.


The Ruby community has taught me so much, it seems the reasonable thing to do. Also, old books tend to go out of print, and then die sad, lonely out-of-date deaths.

Maybe this will keep RBP moving forward, and keep it alive.


BTW, going to experiment with using a wiki page for collecting reader questions. Once I have a few lined up, I'll cut a blog post reply on the RBP blog.

http://wiki.github.com/sandal/rbp-book/questions-for-gregory


Here's a copy on Google Docs. I hate PDFs:

https://docs.google.com/fileview?id=0B8mB_WI1jRkCMzgyZWY5MzY...


I'll run the book through asciidoc's html generator soon enough, but thanks for doing this for now.

Looks like it loses some quality on translation to Google docs, though...


Sweet! Thanks, Greg.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: