Hacker News new | comments | show | ask | jobs | submit login
Your Code is My Hell (avdi.org)
287 points by joshuacc on Aug 22, 2011 | hide | past | web | favorite | 79 comments

Ruby has such wonderful evangelists that somewhere along the line I had gotten the idea that all Rails projects had 100% test coverage, and refactoring was a matter of course. It's kind of a relief to hear that not only do not all projects live up to the ideals, some are the exact opposite.

Knowing that, maybe I can go a little easier on myself when I fail to live up to the ideals, and simply keep on striving without having to feel that I am obviously an inferior coder.

"Ruby has such wonderful evangelists that somewhere along the line I had gotten the idea that all Rails projects had 100% test coverage, and refactoring was a matter of course."

Somehow, I (somebody with zero Ruby experience, and very little web dev experience) had ended up with the same impression. That certainly is some good evangelism.

I taught myself Ruby and Rails on my couch, by myself, over years. But I did it after a more-than-ten-year C/C++ career.

I continued to be amazed and nonplussed that a community could do so well and know so many technologies so fluently when I took so long to learn them.

And now that I've met a bunch of Rails people in person and am considered a Rails guru by two consecutive companies, I'll tell you that yeah, it's mostly hype.

Not that Rails is bad -- it rocks. But it requires a lot of background in deep, messy Ruby to really do well, and there's a lot of bad Ruby and Rails code out there. How could it be otherwise?

Again, it's about maturity. Every popular young platform has a relatively large amount of evangelical fervor and a relatively small amount of real-world long-term experience. Java was no different, years ago.

The two Rails programmers I've met didn't know Ruby itself very well. Also both had to bow out of their contract jobs because neither could build a functioning CMS in less than a year. When I talked to them it sounded like they were kicking ass and getting things done, but they were just vomiting buzzwords apparently.

When I started using PHP ten years ago, I inherited an existing project. Two days into it I thought to myself: "PHP is kind of like Perl, but less useful." I guess I missed the PHP hype phase. If it had one.

I've never been a language evangelist. I just solve problems with whatever technology is most appropriate for the case.

I deal with this at work. The code has never been refactored. There are unit tests for some areas but the views and controllers have so many lines of code and so many unneeded branches that attempting a test there is near impossible now. As soon as I came to the team I've managed to accomplish two minoir refactoring jobs in small areas of the code. I've pitched a more major refactor to the team and now we just need to get it passed our manager.

My own projects generally lack extensive testing (and the code lacks complex code paths) but I refactor heavily.

How can you refactor heavily with minimal unit tests? How do you ensure you aren't breaking things?

Because there is very little code that's affected. Most parts of my app are largely isolated.

The striving is the key. Keep it up!

lol That's exactly the opposite of what the author was trying to accomplish with this article. ;)

I've had my fair share of refactoring legacy code bases as well, and I agree that Rails code is a special kind of horrible. I think you will find that this primarily traces back to the following observation:

Rails developers read GoF and think "object composition is awesome!" Then they go and create a bunch of acts_as_foo and has_bar modules which they mix into their models and controllers, all the while thinking "object composition is awesome!" Meanwhile, it never occurs to them that modules are, in fact, closer to multiple inheritance than object composition, and all the wonderful benefits of object composition that GoF touts are nowhere to be found. Instead, what's left is a giant plate of spaghetti code...

Spaghetti code is procedural-- what you see in Ruby is ravioli code :)

Lemee guess; Bash scripting is shells and cheese?

I don't know if Cushman was referencing this, but it seems reasonably likely: http://c2.com/cgi/wiki?RavioliCode "It has been said that with objects you get RavioliCode: thousands of little classes everywhere and no one knows how to find the places where things really happen."

The best code is lasagna.

True-- but for a lot of projects, all you really need is macaroni and cheese.

So true. Modules are a wonderful feature, but they come with a lot of power, the implications of which are not always easily understood.

To me it's telling that one of the best Rails developers I ever worked with had 10 years of C++ experience, so he was quite wary of multiple inheritance and all its dangers.

I've done my share of cleanups in various languages. I don't agree that Rails code is "a special kind of horrible", but there are a few things that seem to be very common in Rails code. A lot of it seems to be written by inexperienced programmers.

There are any number of indications for making such an observation.

- Multiple different 'Rails best practices'

Rails has changed a lot since version 1.0, but most of the 1.0 stuff still works. People usually learn Rails by reading blog posts and then trying out what they read. As they write code, read more, etc their perception of what the 'best practice' is changes and so does the new code that they write. This makes a mess of things. Experienced programmers will set a 'best practice' for the entire codebase and if that changes for whatever reason will refactor the entire codebase to match the new standard. This is pretty trivial in Rails simply because the codebase of even huge Rails apps is still pretty small.

- Lack of tests

A lack of tests is considered a sin in the Ruby world, but it happens all the time. Embracing testing, I think, is something that comes with time because if you're new you simply don't see the value of it. Lack of testing is bad, but there are also variations on this rampant in the Rails world. Different test frameworks in a single project (I call this the 'follow-the-shiny-new-trend' problem) or simply boilerplate tests that don't really tell you anything other than, yes, this ActiveRecord object without callbacks can be committed to the DB.

- Breaking MVC

This drives me nuts. Doing Model work in the Model except for over here where I do it in the View for whatever reason. This is a clear sign that the programmer doesn't have a good mental model of what's going on and why this is a really bad idea.


Rails embraced JavaScript very early on and is a very fine framework for doing AJAX in. However, doing AJAX is a completely different skillset and most programmers don't have it. Most projects just seem to have various widgets floating around that don't share a common coding or state system. That makes them hard to unify and also means there's a lot of arbitrary XHR calls going into the Rails app (for which there are often 'special case' Controller methods, which don't share a lot of logic with anything else)

- Inconsistent use of the Rails API

This ties into the 'best practices' point, the Rails API is by now pretty big and you have to take the time to study it. New programmers don't usually do this because they don't have a background in programming in frameworks and resort to trial-and-error programming ('hey this works here, let's move on'). Which, BTW, is something that Rails makes very easy.

There are plenty of other things (terrible SQL, custom Ruby code with magic, arbitrary plugins, etc) but those are usually pretty easy to isolate and fix.

The good news is that Rails codebases are generally pretty easy to fix incrementally. Write up a coding standard, start writing useful tests and pick a test framework (really, RSpec is fine) and work from there.

Hard points to manage (which should be obvious and not Rails-specific),

  - If you're fixing an app that is under active development you have a lot of coordination to do
  - This is easier if you can work on-site
  - It's a lot easier if the dev team is small
Rails is a really nice framework and a lot of people are getting a lot of productivity out of it. It's easy to look down on shoddy code and say that from your years of coding experience on the front lines you could do a better job, but that's not how experience is built. If you're reading this you've probably written some pretty horrendous code earlier on. Look at it this way : if a startup can hire an outside consultant to clean up their code and teach them some lessons, then what they had before you came in was apparently enough to get them off the ground and their programmers learned a lot in the process.

That's a good thing, right?

I think the majority of the points here (which are good!) can be summed up with the "You are not a special snowflake" heading.

This particular form of arrogance (let's call it what it is) permeates throughout most sub-communities of CS programming. Ruby is just one tribe. Lispers are another. Game studios, web app developers, a different product team inside the same company, [insert pretty much any other here]... they all believe/rationalize that they are different to Java, and thus have nothing to learn from all those enterprise projects. Even HN has a particular anti-unit testing tribe that will turn up as soon as any article appears that says "Hey, Test Driven-Development is pretty good, huh?" who will complain that testing hurts velocity (it doesn't, see Etsy for a great example).

It's an arrogance that inevitably leads to the issues brought up in the article, taking out a huge technical debt that some other chump has to pay off. I'm not sure how this will ever change. I guess the conclusion is just to try and make sure you're not the chump.

Which side of the example is Etsy supposed to be for that, exactly? They make a lot of changes, and supposedly do a lot of testing, but if you're actually paying attention to what's going on there (and the overlap between Etsy fanatics and people who know what TDD is are pretty small) they have constant stream of small problems. Many people I know who are serious Etsy users complain that the site changes too frequently, has tons of glitches and is confusing - most serious ones were overwhelmed years ago. Just thought I'd let you know since you're using them as an example of something.

I know who are serious Etsy users complain that the site changes too frequently... and is confusing

Well, I used the example in the context of velocity, not necessarily in whether the users are happy ;)

The glitches are more of a concern, but that might be a hardware issue for all we know.

Oh, you meant speed. Velocity includes direction.

No it doesn't. Well, I mean, sure, it does in the context of physics, but in regular English speed and velocity are pretty much the same thing.

On the other hand, velocity has a specific and well-understood meaning within agile software development, and that's what the antecedent post was referring to.

Of course, if you were just being snarky and implying that Etsy are completing plenty of nominal work without it resulting in any actual progress, then carry on :-)

OT, but you remind me of a joke a math professor made in a Calculus III class: A vector has both magnitude and direction, not to be confused with our university newsletter (The Vector), which has neither.

> who will complain that testing hurts velocity

I know you were being intentionally facetious here, but this does nicely encapsulate "How Project Managers and Developers Fuck Agile".

Use of velocity outside of calculating capacity and extra demands being placed on the team is broken; yet I've seen Project Managers terrorize their charges by using it as some kind of fucked-up productivity measurement. For a developer, if your estimates don't include time taken to properly test and develop the user stories, you are lying. You're lying to the business, you're lying to the team, and you're hiding technical debt. As a developer, you get final sign-off on how many points you assign to a story - don't fuck up this 'simple' task :-)

We're not anti-testing. We just think that certain classes of error are better caught at compile time by the type system than at runtime by unit tests, which can even be more lines of code than what they're testing and still miss an edge case.

Not to mention the types of errors that can be avoided by adhering to referential transparency: http://lambda-the-ultimate.org/node/1446

Part of the issue is that whereas unit testing is a good idea to use where possible, test-driven-development is a religion.

Testing is good, but it hurts the ability to churn code quickly. You get stuck with a bad design, because it's too expensive to re-write all the tests.

Now, I think it's good to do a few high-level smoke tests first, to help nut out the interface. (It's much cheaper than having lots of meetings with the boss drawing boxes on a whiteboard, or worse, writing specs). But good test coverage is something that can often wait until fighting bugs becomes a pain point.

I've said it before, I'll say it again - in 5 to 10 years, people will view RoR as they view PHP today - as something that's ruined a lot of programmers.

How much of this sounds familiar?

> “Design Patterns are a Java thing. In Ruby you just write code.”

> “The warnings Ruby produces are dumb; just disable them.”

> In a way I think this is a testament to the power of the platform. If you’re getting a 500 error in a Rails app, you can keep adding kludge after kludge and hitting “reload” until it works. No need to ever write a test or refactor. In languages and frameworks with a lower turnaround time, this kind of tweak-it-till-it-works workflow is simply impractical. Ruby on Rails has an impressively low barrier to fiddling.

That's a bit trite, don't you think?

This is all a religious war, but at the end of the day something like Rails enables you to come a lot closer to "good design, good programming" than many other systems currently in use, i.e. your less than favourite PHP or Java framework here.

I think that someone who honestly believes PHP has RUINED them may be a bit of a tool. We're programmers, we can figure it out.

> at the end of the day something like Rails enables you to come a lot closer to "good design, good programming" than many other systems currently in use

The same thing's been said about PHP.

And sure, PHP ruins programmers like BASIC does - not at all, if they've some brain cells they can rub together. But it does teach shitty habits which can take time and effort to forget.


I'm just not sure there's much to Ruby or Rails that ingrains shitty habits, but I'm biased.

It does give you ample rope to hang yourself with, though.

I don't think that RoR will be looked back upon the same way as PHP. PHP is a language with a buggy interpreter with a ghetto as a standard library with a community consisting of a bunch of script kiddies. </flame>

Ruby on Rails is closer to Java J2EE, in all it's enterpriseyness. It may not use a whole lot of XML files here and there, but it's got a similar flavor IMHO. Lots of stuff happens by "convention over configuration". The XML shit from J2EE is just replaced with a little bit of domain specific code within Ruby.

I can only speculate what's the root cause here. Is the web stack just so horribly broken with session cookies and whatnot? Or the problem of displaying the content of a relational database in a HTML page a difficult problem? Or is it just trying to force a one-size-fits-all solution to a problem that is not well defined?

The underlying issue is the attitude that if you manage to get some sort of process bound to a TCP port that produces HTML, you have nothing else to learn about programming. Then you join mailing lists and say stuff like "the law of demeter is bullshit" and then "unit testing is too hard, so I don't bother". But in reality, if you actually tried to make unit testing easy, then you'd independently discover the law of demeter and most of the other design patterns, and you wouldn't be telling other people that testing is too hard. It's hard because you don't care and haven't tried to do it. Or, you're bad at programming and it's time to pick a new career.

Either way, there is always new stuff to learn. It may crush your ego to learn something new, but in the end, it's a lot easier to read a book or Wikipedia article than to independently reinvent programming. Or maintain that app that's "too hard" to write unit tests for.

That's what I love about Haskell: You'll never get the impression that you learned all there is to it.

Understood monads? Good, try your hand at monad transformers, arrows, continuations, zippers, enumerator based IO...

This is what happens whenever a language is seen as a hot ticket to a good job. It happened with PHP back in the late '90s where thousands of people were learning to program on customer's web projects. The problem is in some cases worse with Rails because of the attitude of _some_ of the leaders in that community which are imitated by too many of the followers; confidence does not always equal competence.

Random: I found out recently that Ruby has no unified test output standard, and that every tool makes up its own o_O

Not only this, but there is active resistance to useing something like TAP.

I've always had a soft spot for TAP's output format. It just seems like it has potential for much better debuggability. The concept of a plan alone puts it above xUnit styles in my mind. That and the expectation that each assertion has a descriptive message just makes it's output seem easier to follow.

Christian Neukirchen (the creator of Rack, among other things) created knock[1], a TAP tool for Ruby. It doesn't seem to have developed much a following, though.

[1] https://github.com/chneukirchen/knock

TAP is nice. I think it could use some extensions... but when I handrolled a test program a few years ago, I chose TAP. It... works.

What is TAP?

One of the frustrating parts of the technology industry is that it moves so damn fast, and people love acronyms. As a result, a new acronym every day.

It looks like an interesting tool, I'll investigate it.

TAP has been the testing protocol for the perl core since the late 80s :)

> As a result, a new acronym every day.

You are being optimistic.

    "Dividing methods into private and public is for control freaks, you don’t need it in Ruby"
Coming from a Python dev's perspective, does Ruby have the concept of public/private? Python just has the underscore and double underscore conventions: if you absolutely have to you can use the "private" variables and methods, but in doing so you still understand that it might change without notice in future versions. "We're all adults here" is the the motto, if I recall.

Ruby has a public/private divide which can easily be circumvented. But the circumvention is a bit ugly, which is a good thing - it makes the circumvention obvious in the code.

Personally, I think Ruby hits a sweet spot here: the privacy curtain is strong enough that Ruby WILL let you know when you've stepped off the path of using methods which are part of the stable API. But you can then tell Ruby "yes, I understand, and I know what I'm doing".

Strictly speaking ruby, private only forbids calling the method in question with an explicit receiver.

object_instance.private_method is obviously prohibited, but so is self.private_method when called from another class method. The latter one is fine when private_method is called, without the self. To circumvent privateness in the other case ("calling an underscored method") you would have to say object_instance.send(:private_method).

Luckily there's also protected, which allows you to call with an explicit receiver, but only from an instance of the same class (or within its hierarchy).

Yes, Ruby has private methods:

    def i_am_public

    def i_am_private
    private :i_am_private


    def i_am_also_private

Didn't Zed Shaw predict this before?

Anyhow, personal recent experience on using a plugin that is an API to some webservice:

- Lots of static methods

- Code structure is awful

- Documentation is non-existent

... and I rarely use Rails let alone learn Ruby yet I can see how horrible that plugin is.

I heard I'm not alone when it comes to the discussion of the quality of plugins out there.

I don't mean to bad-mouth Rails framework because it is a well-thought project (let's not discuss the internal code). But as many who have been in this industry for a while, we kind of know that this is coming sooner or later.

Python code, in many places, seem to have a good balance of pragmatism, UNIX culture, and discipline.

Having said that, Rails 3.x seems to mark a change in attitude from the Rails core team. They're starting to address issues and stabilize the framework for the better. Let's hope the rest would clean up as well.

From the article: "In fact, offhand I can only think of one commercial greenfield Ruby project I’ve participated in."

I can say the same of C, C++ and Python in the context of my personal work history. Is Avdi's experience so unusual in the Ruby world? It seems that most development jobs primarily involve working on other people's code if the language has been around for any time at all.

Edited to add: I've also seen everything listed in the "But Rails is different!" section in every commercial project I've ever worked on. I suspect most developers that just want to hack away on commercial code without any discipline end up having nearly the same set of justifications, no matter what language they're using. (Especially that bit about ignoring/disabling warnings.)

With a young language, the ratio of people working on greenfield projects to legacy projects is going to be higher than with an old language.

Sure, but that's why I mentioned Python: Ruby is only 4 years younger than Python, but I haven't run across that many greenfield projects that involved Python.

Of course maybe I just haven't looked in the right places. :)

And they're both older than Java, but you're more likely to see greenfield projects in Ruby or Python than Java because Java gained acceptance much more quickly. The year that the first compiler for a language was written is kind of irrelevant — it's time at certain popularity thresholds that really determines a language's maturity.

I think Python developed a little bit more gradually. Ruby was a little slower at first, and then shot up like a rocket.

I have no data to back that up, but it certainly seemed to happen that way.

Is it significant that Ruby on Rails seems frequently learned "framework first, then language" -- many people first learn Rails, then go to work learning Ruby. Whereas I think frameworks like Django are learned by people already familiar with Python. It's possible that folks can more easily get through the hassles and design questions posed by unit tests with a better grounding in the underlying language.

> Django are learned by people already familiar with Python

If only that were true. Like the OP I've been working with mostly legacy Django codebases the last few years and much of the same issues he raises could be applied to those projects as well.

Ruby exploded really only after the web stuff happened. Python caught on before that.

The product I am currently working in aims to keeps all it's components small enough, so that you could rewrite each one of them in a few weeks at most.

When a daemon gets too big, we split it up.

That makes each writing each new part feel much more like a green-field exercise than you'd expect from a product this size.

Greenfield projects and projects < 1 year old are very common right now.

Let that be a call for people to develop a) Rails experience b) testing, refactoring, working with legacy code experience

Because it means that there are going to be a lot of 3+ year old projects in 2 years that will need those skills.

I think it depends a lot on age/experience and how you got into development. In my case, I'm relatively young, lack a college degree, and started out in sysadmin and, shortly thereafter, operations engineering roles. I've discovered good ops people are a pain in the ass to find, and I had the misfortune to be very good at it, so I naturally keep getting already-"running" systems dropped on me with instructions to maintain, fix, and interface with them. I've only had one true greenfield project (still in progress) since I started getting paid for my work.

Most of the other developers I know are older, have computer science and/or EE degrees, and have been around the startup block a few times, and have several greenfield projects under their belt.

Worth it all for the subsection titled, "You are not a special snowflake"

My take on this is that the excesses of enterprise Java poisoned a generation of programmers' opinions about static typing. We're going to see people start rediscovering the benefits of static typing in a sane framework as more of these kinds of projects hit the wall.

I've been doing rails for a long time.

My biggest advice, use rails plugins sparingly, and when you do ensure they are well maintained. Nothing is worse than an old rails project that is using 20 plugins and you need to update it to a new major version of rails.

So very true. Almost all the misery of maintaining and upgrading a Rails app comes from the interactions between plugins, each other, and Rails.

Why is this? I don't know exactly. It could be the way that plugins tend to manipulate multiple layers of the stack and tend not to use clearly-defined public interfaces. I suspect that more of it is the fact that plugins are the self-promotion tool of choice for the jobbing Rails developer: they fall from grace as quickly as they rose, abandoned by the developers to whom they gave a leg up, supplanted by a new, different plugin with an obviously - everyone agrees! - much more correct way of solving the problem. Meanwhile, your old Rails app is dependent on a dozen pieces of decrepit abandonware.

    Why is this? I don't know exactly.
Monkeypatching! It's all fun and games until someone else decides that monkeypatching #poke is also what their gem or plugin needs. Then, of course, you start accidentally #poke'ing your Eye when you least expect it, and cannot figure out from where or why...

I wish I had 10 accounts so I could give you 10 +1s!

I guess those who forget the past are doomed to repeat it.

It's funny, principles of good programming are about as old as programming itself and yet everyone seems to feel the need to rediscover them periodically.

If you look at some great nix utilities, they should "do one thing and do it well". Hm, sounds like Single Responsibility Principle.

Dependency injection? Funny, reminds me a lot of nix pipes.

So to me the moral of the story is: programmers with experience and an open mind will tend to make good decisions. Inexperienced programmers just need time if they're willing to learn.

And arrogant jerks will always make messes.

Yes. Though principles tend to change. For examples Unix' "No news is good news" started because IO was slow via teletypes. Now we keep it, because it makes piping easier.

Also nowadays you can afford to value simplicity over speed in so many more places.

Not sure that's a good title for the content...

But it's certainly true that sloppy code can be produced in any language.

To me what the article is highlighting is a certain blindness on the part of many Ruby coders. My first language of choice is Ruby, so I'm not trying to spread hate here.

That said, I've encountered many times where a Rubyists first reaction to anything Java is "oh that's terrible" but anything implemented in Ruby must be beautiful, just because it's Ruby. Clearly, that is not true and yet people believe it over and over again.

Also, if you look at a lot of the developments in the Rails world over the past few years, they've been running into problems long solved in the Java world, but only now have Ruby projects been getting big enough to warrant general purpose solutions.

A perfect example is Bundler. It's basically Maven for Java, but it took the community a while to realize "hey, managing libraries is a pain, we need a tool for it".

So as Rails grows up, hopefully it won't forget that many other communities have solved similar problems before it.

Sometimes it's almost as if Rails invented the Internet. :)

That JS on page crashes IE8.

Or as I like to put it when filing bugs:

IE8 crashes trying to run this JS code.

I had someone else report an IE7 crash recently. I wish I could narrow down WHICH JS is the problem. It's just stock WordPress+various widgets.

Yikes - IE 8 really crashes. Here's the console output from IE9 developer tools when changed into IE8 Standards mode:

  SCRIPT438: Object doesn't support property or method 'indexOf' 
  github-badge.js, line 59 character 7

  SCRIPT438: Object doesn't support this property or method 
  badge.js, line 47 character 7

  SCRIPT438: Object doesn't support property or method 'getElementsByClassName' 
  embed.js?pname=wordpress&pver=2.61, line 35 character 78

  SCRIPT438: Object doesn't support property or method 'getElementsByClassName' 
  embed.js?pname=wordpress&pver=2.61, line 35 character 78
So, hopefully that gives you a starting point. The IE9 developer tools are much improved over previous iterations.

Interestingly enough, when I switched it into IE7 mode, only the first two errors appeared.

Thanks! I've queued this for a closer look.

Its still crashing..

I would be interested in a comparison with Python projects.

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