Your Code is My Hell

It occurred to me recently that my experience as a Rails developer may be somewhat unique.

I often get brought in to help preexisting Ruby/Rails projects evolve and mature in a sustainable way. As a result, the vast majority of Ruby projects I’ve worked on have been well-established by the time I arrived. In fact, offhand I can only think of one commercial greenfield Ruby project I’ve participated in. All the rest have been “legacy” from my perspective, in the sense that there was a sizable codebase in production before I showed up. (I’m not counting personal and internal projects.)

I’ve realized that in this my experience may be somewhat unusual among Ruby and Rails developers. With its fast churn and startup-heavy community, a lot of Rubyists are working on projects that they started recently.  My work is more in the codebases where the original programmers have moved on.

Rails’ dirty secret

In the days before I got paid to write Ruby, I worked on some legacy codebases that had histories spanning multiple decades and 100s of KLOCs. That’s a lot of opportunity for bad code to accumulate; and in some cases, the accumulations were impressive.

But here’s the dirty little secret of Rails development: the messiest, nastiest big-ball-of-mud code I have seen in my entire career has been in Ruby on Rails projects. I’ve seen Rails projects that accumulated enough technical debt and waste in two years to make 10 year-old C/C++ programs look clean and elegant by comparison. And it wasn’t just one project. I’ve seen it over and over.

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 slower turnaround time, this kind of tweak-it-till-it-works workflow is simply impractical. Ruby on Rails has an impressively low barrier to fiddling.

Unfortunately, as a result a lot of projects I come to on have hit what I think of as the productivity crash. At some point the cumulative effect of all those little shortcuts catches up with the development team, and changes that would once have taken a day start taking two weeks as all the dependencies and unintended consequences are sorted out.

As a somewhat ranty aside: this is also the point where, often, original members of the team start moving on to bigger and better things. Meanwhile the crew that inherited the codebase is left to field questions from management about why they can’t seem to push out changes nearly as fast as the old team. The new team is confronted with the problem of getting the codebase under better test coverage and a little more modularized before they can ramp velocity back up; thus perpetuating the notion among the business types that testing and refactoring just slows things down. And/or that the original team were some kind of wizards.

Okay, rant over.

But Rails is different!

Rails developers are sometimes accused of being arrogant and judgmental. I’m not sure how true this is; I don’t see it all that much, but maybe I’m too close to the community and/or arrogant and judgemental myself to be a fair observer.

What I do see is a kind of “Rails exceptionalism”.  Remember back in the first dot-com boom, when some economists were saying that no, this time it was different, the Internet had changed the game this time the markets would just keep going up and up? The phenomenon I see is similar in spirit. it’s a belief, perhaps not fully conscious, that Ruby on Rails development is somehow different, and not subject to the forces affecting other software projects.

Here are a few examples, just to give you an idea of what I’m talking about:

  • “Design Patterns are a Java thing. In Ruby you just write code.”
  • “The warnings Ruby produces are dumb; just disable them.”
  • “Sure they aren’t technically Unit Tests, but isolating objects turned out to be kind of hard and besides nobody else is doing it.”
  • “Monkeypatching is frowned on in other languages, but in Ruby it’s fine. The downsides almost never materialize.”
  • “Stuff like the Law of Demeter isn’t really as important in Ruby code”
  • “Dividing methods into private and public is for control freaks, you don’t need it in Ruby”
  • “That’s only a code smell when it’s in Java code”
  • “That’s only a problem in large projects” (implying that this project will never become large).

I also see a fair amount of project or subsystem-level exceptionalism: “I know they say classes shouldn’t be this big, but for this class it just makes sense for all of that stuff to be in one place”.

Welcome to Lilliput

The truth is, Ruby on Rails projects are exceptional in a way: they are really small. In James Gray’s terrific keynote at Lone Star Ruby Conf this past week, he mentioned “huge projects” of 40+ KLOC. That gave me a smile, because the first two Rails projects I was ever paid to work on were 50KLOC and 70KLOC, respectively. And while that may seem like a lot of code, that’s small by industry standards.

There are a few reasons for this. Ruby is a more expressive language than, say, Java, so to some degree Rails projects will always be smaller than equivalent projects in higher-ceremony languages.

It’s also possible that Rails programmers have embraced the wisdom of breaking systems many, small, intercommunicating apps. I’d like to believe this, but experience suggests this strategy has seen only spotty uptake.

No, I think the biggest reason for the diminutive nature of Rails apps is also the most obvious: they are all pretty young. It’s a young framework, and there’s a lot of churn in this community. A Rails app that lasts three years is ancient.

I think it’s safe to say that this situation won’t last. We’re going to see larger and larger codebases. And here’s a not-very-daring prediction: a lot of projects are going to hit the very same architectural roadblocks that Lisp, Smalltalk, Pascal, C++, and Java projects hit before them.

You are not a special snowflake

It’s funny reading programming literature from the 80s. Dynamic, object-oriented systems navigating the transition from “small” to “medium-sized”. Sound familiar?

Every revolutionary believes his revolution is special, and won’t devolve into the partisan bickering and venal bureaucracy that the last revolution led to. And it’s easy to believe at first. Everyone’s excited and eager to help; the problems are relatively small; and the marketing drones haven’t latched onto the movement yet.

The truth is, the problem you are solving probably isn’t as special as you think it is. And those byzantine patterns you thought were a relic of a bygone age were invented by people using languages surprisingly similar to Ruby.

Don’t panic

Relax. I’m not here to tell you that the last few years were all just a lovely dream, and you’re really still strapped to a chair in the Ministry of UML.

Ruby is still a wonderful language, and the terrific thing about it is that it adapts to large-system design patterns remarkably easily, and with very little ceremony. Dependency Injection? It’s a one-liner. Object delegation and composition? Piece of cake. Contrary to misconceptions, Ruby doesn’t obviate solid design patterns and SOLID principles; what it does is make them very easy to express. In fact, the ease of expressing robust architectural styles was what attracted some of us early-adopters to the language in the first place.

Just please, do me a favor: before you tell me that Ruby and Rails doesn’t need any of this discipline, have a chat with the guy or gal who is still maintaining the first Rails app you worked on.

125 comments

    1. I came to Ruby (partly) via Perl, but it’s true I never maintained a large Perl system.

      That said, one of the interesting things about the Perl community was that there was such a strong consciousness of how easy it was to write bad Perl, there was a lot of social pressure to do things “right”. For instance, it was just about mandatory that you ran your code with the -w flag. Ruby hasn’t fully inherited that kind of quality-oriented peer-pressure.

          1. Sorry – I was basically adding on to our previous conversation on the subject.

            Worth “everyone using -w”. Because, as we saw, if I use it, but no Gem authors do, I’m going to have to weed through a lot of false positives to find the warnings in my own code.

            Maybe that weeding would be worth it, except that there is no option to disable certain warnings or certain warnings in certain sections. I would like to use -w, but I’m damned if I let two characters on the commandline dictate what correct code is.

            But hey – you tell me. I thought the conclusion of that previous conversation was that you don’t use -w yourself.

        1. Disagree. I would be willing to bet that at least a quarter of new rails programmers don’t even run a ruby program outside of the rails stack in their first 6 months of writing rails code. I think people come to a framework like rails and find a great deal of comfort in it, but it’s easy to forget that there’s a whole world out there, outside the walls of the framework. I know I certainly found myself in this mindset at one time.

          Maybe we’re saying the same thing here, but I think the larger issue is many of the new members of the rails community often fail to learn to write idiomatic ruby code because it’s just so damn easy to crank things out in rails. 

      1. Actually, the next language I really consider learning is Perl 6. I came to Ruby from Perl 5 (talking about web programming) but Perl 6 seems to be really amazing with some greater features than Ruby. A completely different beast from Perl 5 🙂

        And about your situation, I know exactly what you mean, but in my case I’ve been dealing with Grails projects in the last 2+ years in two different companies, none of them having a single written test.

        And, in both, the situation was the same as you described: the timing was for too much technical debt that my new developments were much slower than the initial development which makes the first cowboys developers highly evaluated while making us low productivity programmers 🙂

        How you can show all refactoring you’ve been doing without a single written test to your non-technical employee? And how to sell him the idea that it would be great if he could give you some time for writing at least the more basic tests before your refactoring for avoiding breaking applications under production? How to convince them that the time you’re spending refactoring the application will yield to much faster development in the future? If you have the answers for these questions, please share 🙂

        Cheers and congratulations for another good post!

  1. yes, it is very easy to make a mess in Ruby, but it is very easy write good looking code in Ruby. The second is more valuable for me.

  2. yes, it is very easy to make a mess in Ruby, but it is very easy write good looking code in Ruby. The second is more valuable for me.

  3. It seems to me that every new, “better”, “faster”,”shinier” technology attracts the worst kind of lazy. 
    Maturity then clears the cruft.Programmers are lazy, but you can be ‘smart lazy’ or ‘stupid lazy’ (we can hash out a terminology later, bear with me here).Stupid lazy is the mode you’re describing here: hack something together until it runs and let your successor worry about the consequences all the while using up the free time. Note that you can get the same effect with lack of experience (I guess all of us cringe at our initial attempts at coding) and the only difference is that the former is inexcusable while the latter can be improved. Now I’ve found that in order to satisfy my innate laziness I have to…work. Work at learning proper development practices, design patterns, TDD etc. It saves on the boring, repetitive and relation destroying aspects of developing software.What attracted me to Ruby in the first place was the fact the the language gets out of my way and allows me to actually devote time and grey matter into learning these things.It also makes it easier to figure out which kind of lazy the programmer next door is: you can’t sink time behind the semicolons and the curly bracers anymore.

    1. I think this nails it. Ruby/Rails at one time makes it  very easy to write good code and very easy to write bad code. What you actually get is going to depend on the nature of the developer – “lazy” or “smart”.

    2. “It seems to me that every new, “better”, “faster”,”shinier” technology attracts the worst kind of lazy.”

      I haven’t seen that at all. However, I have certainly heard it said many times. I heard it in 1989 in response to C++. I heard it in 2000 in response to XP and TDD. And I certainly heard it in 2005 in response to Ruby on Rails. In each case, I found the best kind of lazy developers jumping on what was clearly an improvement over the status quo. The worst kind of lazy programmers just plodded along with their C, waterfall, or JSP skills.

      It take energy and “good laziness” to learn new technologies. Because good lazy people are always looking for ways to make their job easier.

      1. Ah, but you are talking about “new” things that were trying to establish the aforementioned best practices in design, process etc. I would have to agree and concede the point.
        A better analogy for what I was thinking would be VB – the snake oil of RAD. And Rails did add that RAD shine to Ruby which in turn attracted that VB type of developer.

      2. Ah, but you are talking about “new” things that were trying to establish the aforementioned best practices in design, process etc. I would have to agree and concede the point.
        A better analogy for what I was thinking would be VB – the snake oil of RAD. And Rails did add that RAD shine to Ruby which in turn attracted that VB type of developer.

    3. “It seems to me that every new, “better”, “faster”,”shinier” technology attracts the worst kind of lazy.”

      I haven’t seen that at all. However, I have certainly heard it said many times. I heard it in 1989 in response to C++. I heard it in 2000 in response to XP and TDD. And I certainly heard it in 2005 in response to Ruby on Rails. In each case, I found the best kind of lazy developers jumping on what was clearly an improvement over the status quo. The worst kind of lazy programmers just plodded along with their C, waterfall, or JSP skills.

      It take energy and “good laziness” to learn new technologies. Because good lazy people are always looking for ways to make their job easier.

    4. “It seems to me that every new, “better”, “faster”,”shinier” technology attracts the worst kind of lazy.”

      I haven’t seen that at all. However, I have certainly heard it said many times. I heard it in 1989 in response to C++. I heard it in 2000 in response to XP and TDD. And I certainly heard it in 2005 in response to Ruby on Rails. In each case, I found the best kind of lazy developers jumping on what was clearly an improvement over the status quo. The worst kind of lazy programmers just plodded along with their C, waterfall, or JSP skills.

      It take energy and “good laziness” to learn new technologies. Because good lazy people are always looking for ways to make their job easier.

  4. I honestly think that a lot of people who came to Rails at first were coming from PHP and Perl which doesn’t have the kind of OO discipline that languages like Java, C#, and C++ do.

    More recently, I’m meeting more and more people that are coming from Java/C# to Ruby specifically for development in Rails and are bringing their OO disciplines with them.

    I hope that we start seeing more maintainable legacy Rails projects from developers who know OO well sharing their knowledge with everyone else and teaching these patterns and disciplines in Ruby.

    Great post!

    1. I think you’re right. I also think it’s unfortunate that a culture of quality, discipline, or even just of sharing patterns is a language-specific thing. Good habits are orthogonal to languages.

      1. On C#, the percentage who are really interested in quality/discipline/etc is probably far lower than you might expect. The information is out there but most enterprise developers don’t really care.

        1. I call bull shit on this point. I don’t know ANY C# developers who don’t care. Most of them don’t “get it” and wouldn’t even know where to start participating in a community, but they do care about their work and they think they are producing high quality software. The fact that a mega-corporation and army of tool vendors are coddling and spoon-feeding the development community leads to lower quality code in most cases, but to accuse the developers of not giving a shit is an arrogant stereotype that the Ruby community should just drop.

          1. I’ve seen a tonne who’ve not cared, so yes I stand by the fact that its lower than you’d expect.

            As you say there’s also other issues.

      2. I don’t think good habits are orthogonal to languages. People who care about good habits care about the language they use, and they actively seek out the best language for the job. At the current time, the best language for Web applications is not PHP, it’s not C# – it’s (probably) Ruby.

        Why would a truly good programmer want to use C for general-purpose programming? How many truly bad programmers moved to Smalltalk? Who could have respect for a Visual Basic programmer in this day and age?

        There is definitely a relationship between one’s choice of language and one’s skill, I believe.

        1. Statement: developers who care about good habits choose the “best language for the job”.

          Assumption: That developers “who care” are motivated solely by technical rightness for a particular task.

          Where this breaks down: 
          – You work at Google then you’re using C, Go, Python, or Java.  Ruby?  Nope.  Maybe you work at Google because you love the Google culture.  And you still may care about good habits.
          – You actively contribute to language X.  Dedication to language X may cause blindness with regard to language Y that may better solve your problems.

          Conclusion: Good habits are at least, in part, orthogonal to language.

        2. Statement: developers who care about good habits choose the “best language for the job”.

          Assumption: That developers “who care” are motivated solely by technical rightness for a particular task.

          Where this breaks down: 
          – You work at Google then you’re using C, Go, Python, or Java.  Ruby?  Nope.  Maybe you work at Google because you love the Google culture.  And you still may care about good habits.
          – You actively contribute to language X.  Dedication to language X may cause blindness with regard to language Y that may better solve your problems.

          Conclusion: Good habits are at least, in part, orthogonal to language.

    2. Maybe I’m wrong but a few years ago my first impression of Ruby was it was a mix of people who get it all (OO/TDD/etc) and people who’ve maybe come in from PHP who (at the time) weren’t so into that stuff.

      The C# community was definitely watching what the first group of guys were saying on TDD and other such issues. We copied mercilessly. So whilst I agree there’s still people leaving .NET/Java to come to ruby I’d be surprised if there aren’t already the people inside the Ruby “community” solve this knowledge gap.

      In fact I wonder if the problem is the guys who are into patterns/OO/TDD etc just dont’ consider it interesting to blog about and/or disucss them anymore?

      1. Or maybe we’re not looking hard enough 🙂
        It’s also a fact that the intense conversations around patterns and OO are a thing of the past.
        With the advent of dynamic languages and functional programming I’ve noticed an uptake in discussions but IMHO we’re still trying things out in those fields – and often reinvent the wheel.
        As I trained as an engineer I found the sheer volume of CS knowledge intimidating (geez, do I have to study all over again?!?). I now enjoy it 🙂
        And here’s a couple of links on patterns and languages (only as a tangent)

        Dominus on Design Patterns http://blog.plover.com/2006/09/11/, response by Johnson (Ralph, the Gang of Four one) http://www.cincomsmalltalk.com/userblogs/ralph/…, clarification by Dominus http://blog.plover.com/prog/johnson.html and the language – library distinction by Miles Gould http://pozorvlak.livejournal.com/94558.html

        1. Yes
          its out there if you want to look especially if you look accross
          communities/languages/technologies. Given all that, and the wealth of
          information in books/Web/videos you’d think there’d be no need to cover
          this sort of stuff again, but my experience is thats not been the case
          at all. Really I think the best approach in terms of written content is
          small up to date blog posts. I know that works for others and it
          definitely works for me, especially if its well researched and links to
          further content (books/videos/articles) where appropriate.

          On one thing, I have noticed one issue common to .NET and Ruby, not
          enough devs look at other communities. For example I read a blog post
          about duck typing a week or two ago and it was clear the developer
          wasn’t aware of how you’d solve the same sorts of problems in Java/C#.
          Thats definitely a missed opportunity.

          I also agree intense OO conversations are over, but thats not really the sort of thing I was talking about.
           

          1. I’ve crossed disciplines for over 20 years, and this problem is endemic. It’s not a Ruby problem, it’s not a programming problem, it’s a human problem.

      2. That is my impression, too, Colin. Add to that the Smalltalk refugees, who fell on Ruby like starving peasants on a loaf of bread.

        I too came to Ruby/Rails from C#. And it was like, “oh, I don’t have to do ORM mapping?”, “you guys think testing is of primary importance?”, “database migrations?”, “everything’s an object (practically)?”, “you’ve heard of patterns?”, “everything’s open source?”, “you use what? Macs?”, “etc., etc.?” A no-brainer to recreate my career and give up Microsoft/C#.

    3. Ruby community lacks discipline and somehow PHP is to blame?  That’s horse shit.

      We in the PHP community suffer from the same lack of knowledge and discipline as any other dynamic language. The only reason Ruby thinks it’s different is because it’s only now having been around long enough to attract a larger representative portion of under skilled developers.

      I’ve worked in 750 KLOC PHP environments and I don’t see how blaming the language is going to help keep messes like that from ever happening again.

      When you put a 10 lb feature in a 5 lb bag in a 3 lb bag, it doesn’t matter what the bag is made of. The problem is not the technology itself, but rather your misuse of the technology.

      What the industry really needs right now in both Ruby and PHP is better access to and emphasis on software architecture training and education.

      Every person I’ve ever met with the title of Software Architect who works primarily with PHP has demonstrated a surprisingly poor grasp of the discipline of software architecture.

      1. sure PHP is to blame. as was mentioned above – low barrier means low quality developers. PHP was the first and lowest barrier for most of web developers. Ruby on Rails at least tries to enforce some good practices and patterns on those who come from PHP.

        1. And what of novice developers who come to Ruby without extensive experience in PHP? I guess my point is that the reason novice developers write bad code is because they are novice developers, not because they are PHP developers.

          1. you’re absolutely correct. all we need to do now is compare popularity of php and ruby as web development platform. quick googling says there’s 10 times more results for “web development php” than for “web development ruby”. we can almost safely assume that there are 10 times more novice developers coming to php than to ruby and therefore 10 times more switching to ruby from php than to php from ruby. that’s all stretched and approximate but you get my point.

          2. I don’t think the ratio of attrition from php->ruby vs ruby->php is at issue, it’s the ratio of novice php->ruby vs all novice->ruby. More developrs, especially recent grads, are picking up Ruby as a first language. We need to do a poll.

          3. I don’t think the ratio of attrition from php->ruby vs ruby->php is at issue, it’s the ratio of novice php->ruby vs all novice->ruby. More developrs, especially recent grads, are picking up Ruby as a first language. We need to do a poll.

          4. but that information would be useless without knowing amount of crappy code produced by each group of devs.. how about simple question to rails devs: “has switching to ruby on rails improved your overall quality of code and understanding of importance of design patterns?”

          5. Hello, I’m not a specialists like you. Currently I’m doing Masters Degree in Computer Engineering. Actually technology is being blamed here but the programmers should be blamed, if they were nicely trained and they’ve nicely learned, they would’ve been good developers by now. I’m saying it from practical knowledge. I can see many students around me who only study theory and brings good result without actually having any practical knowledge. Whenever they’ve to submit their project reports they run for others for help. What kind of programs they will do after they join any company! I’ve a doubt. So please finish this debate. Technology is good in their respective places, users are misusing it, that’s the fact.

          6. but that information would be useless without knowing amount of crappy code produced by each group of devs.. how about simple question to rails devs: “has switching to ruby on rails improved your overall quality of code and understanding of importance of design patterns?”

        2. And what of novice developers who come to Ruby without extensive experience in PHP? I guess my point is that the reason novice developers write bad code is because they are novice developers, not because they are PHP developers.

        3. And what of novice developers who come to Ruby without extensive experience in PHP? I guess my point is that the reason novice developers write bad code is because they are novice developers, not because they are PHP developers.

        4. And what of novice developers who come to Ruby without extensive experience in PHP? I guess my point is that the reason novice developers write bad code is because they are novice developers, not because they are PHP developers.

        5. I think the history of Java (lol @ java/C# devs having any more OOP discipline than people from other backgrounds btw – haven’t seen much C++) is proof positive that you cannot enforce good practices through language or tool design. Anybody who thinks public vanilla getters and setters are somehow preserving encapsulation better than public properties should have their IDEs slapped out of their hands until they’ve learned to value maintainability and how to actually think about what they’re doing from the act of writing code without one. The problem is that there will always be people who don’t want to learn more than they have to and there will always be people delighted to pay them less now so somebody else can pay ten-fold later.

    4. Ruby community lacks discipline and somehow PHP is to blame?  That’s horse shit.

      We in the PHP community suffer from the same lack of knowledge and discipline as any other dynamic language. The only reason Ruby thinks it’s different is because it’s only now having been around long enough to attract a larger representative portion of under skilled developers.

      I’ve worked in 750 KLOC PHP environments and I don’t see how blaming the language is going to help keep messes like that from ever happening again.

      When you put a 10 lb feature in a 5 lb bag in a 3 lb bag, it doesn’t matter what the bag is made of. The problem is not the technology itself, but rather your misuse of the technology.

      What the industry really needs right now in both Ruby and PHP is better access to and emphasis on software architecture training and education.

      Every person I’ve ever met with the title of Software Architect who works primarily with PHP has demonstrated a surprisingly poor grasp of the discipline of software architecture.

  5. For me, it’s hard to see Rails code as worse than legacy code I’ve seen in other languages.  I’d agree that it is superficially worse.  The biggest problem is distributed information.. you have to look in many places to see what is going on.  Nicely, though, you can get information faster than you can in static languages, so it is much better in that respect.  It makes recovery easier.

    1. I think a possible factor in my perspective is that I came from aerospace C++/etc. to enterprise/web Ruby. It’s possible if I’d been immersed in the enterprise Java world beforehand I would have had a different experience.

      1. I was thinking about the distributed information thing yesterday… I have to have more files open when I’m working on a Rails feature than I’ve had to have in any other framework. Not sure how I feel about that.

        1. You could be right, on the plus side on that front though:

          1. The conventions around the files mean IDE can help you navigate between related files.
          2. From what I’ve seen Ruby/Rails results in far far less code than in .NET, especially as there are so many good gems.

          However I think with bigger codebases navigation becomes a bigger concern. On that one issue I do have with Ruby is the lack of any sort of interfaces. I know they don’t fit such a dynamic language and without a compilation step their use is limited.

          However I find in C#/Java they can make the intent of code clearer and make navigation simpler. Being able to see that a method depends on such-and-such an abstraction, and then track back to what implementations there are does seem to me to make reasoning about the system much simpler.

          Sometimes that explicitness and implied constraints can be a problem, but in some cases its well worth it. Anyway Interested to find out if I’m right to be concerned about it or whether I’m barking up the wrong tree.

        2. You could be right, on the plus side on that front though:

          1. The conventions around the files mean IDE can help you navigate between related files.
          2. From what I’ve seen Ruby/Rails results in far far less code than in .NET, especially as there are so many good gems.

          However I think with bigger codebases navigation becomes a bigger concern. On that one issue I do have with Ruby is the lack of any sort of interfaces. I know they don’t fit such a dynamic language and without a compilation step their use is limited.

          However I find in C#/Java they can make the intent of code clearer and make navigation simpler. Being able to see that a method depends on such-and-such an abstraction, and then track back to what implementations there are does seem to me to make reasoning about the system much simpler.

          Sometimes that explicitness and implied constraints can be a problem, but in some cases its well worth it. Anyway Interested to find out if I’m right to be concerned about it or whether I’m barking up the wrong tree.

        3. You could be right, on the plus side on that front though:

          1. The conventions around the files mean IDE can help you navigate between related files.
          2. From what I’ve seen Ruby/Rails results in far far less code than in .NET, especially as there are so many good gems.

          However I think with bigger codebases navigation becomes a bigger concern. On that one issue I do have with Ruby is the lack of any sort of interfaces. I know they don’t fit such a dynamic language and without a compilation step their use is limited.

          However I find in C#/Java they can make the intent of code clearer and make navigation simpler. Being able to see that a method depends on such-and-such an abstraction, and then track back to what implementations there are does seem to me to make reasoning about the system much simpler.

          Sometimes that explicitness and implied constraints can be a problem, but in some cases its well worth it. Anyway Interested to find out if I’m right to be concerned about it or whether I’m barking up the wrong tree.

          1. “On that one issue I do have with Ruby is the lack of any sort of interfaces.”

            This may not give you all the benefits you’re looking for, but I tend to use RSpec shared example groups to define interfaces that multiple classes implement.  The great thing about it is that it tests not only the structure of the interface (i.e. that the class has certain methods) but also the behavior of it.

          2. Interesting.

            I did consider mentioning tests and agree they make excellent
            documentation. However I don’t think they necessarily replace well
            designed abstractions, not least as having to look up/at the tests adds
            overhead when quite often you just want to get an idea of the
            responsibilities/collaborations between the objects.

            On structure vs behavior, I realise that but a well designed interface
            will usually make the behavior clear. For sure the details will end up
            in the documentation on the interface and the tests, but the interface
            will tell you a lot on its own.

            On the shared examples…say we have an argument coming in to a method.
            If the argument is a well designed interface I can see at a glance what
            its responsibilities are, and what I can do with it. I’m not necessarily
            interested in the classes that implement that interface at this stage.

            Whats the best approach there (honest question, not worked on a large Ruby codebase)?

          3. Every time we perform a message send in Ruby we’re asserting an implicit type interface with the target object handling type failures via method_missing or else handing the responsibility back to the call site via an exception.

            Naively this could be generalised by filtering on a series of responds_to? messages but we can never provide the same runtime guarantee that’s present in Java or C# as between message sends it is entirely possible for a Ruby object to drastically change its behaviour. Type in Ruby is therefore subject to an uncertainty principle which is at odds with these kinds of static guarantees.

            This isn’t to say that a traditional interface mechanism couldn’t be added to the language, but for it to be authoritative it would have to be atomic in operation and enforce a global lock on code mutation throughout the guarded critical code section.

          4. Yup, wasn’t suggesting you could use traditional interfaces given Ruby is so dynamic.

            What triggered my comment was the bit about “Ruby on Rails projects are exceptional in a way: they are really small”, my thinking was just that as they grow (and maybe more importantly age) do traditional interfaces begin to come into their own?

        4. By the way: that’s a symptom of loose coupling.  Is it a smell?  Perhaps.  Your application’s behavior is easier to change.  However, there is increased cognitive load in having to recall that “behavior X is in class Y” when there are many more classes.

          I don’t need to speak much to the antithesis as it’s already known as “spaghetti code”.

          1. I’m not sure that’s true. I’ve seen model/controller/view/partial stacks that were all incestuously coupled. I think coupling is at least partly independent from number of files.

          2. A sniffle is a symptom of both the common cold and allergies.  That doesn’t make it less a symptom.

            Of the spaghetti apps that I’ve seen, some of that have had an explosion of files.  Some of that is also due to frameworks, if used, and not the spaghetti code.

            Of course, the conclusion is “it depends”. 😉  It so often is.

  6. I’m new to Ruby but haven’t seen as much discussion as i’d have expected on OO but also larger system architecture, domain modelling,  and so on. Be good to see more posts on these fundamentals for sure.

    1. It’s a bit ironic, given that Ruby is more fully OO than any still-popular language out there. Personally, I think if you don’t grok OO you’re unlikely to draw out Ruby’s full power.

  7. My entire 5-6 years of Rails experience has been middle ground. I’ve built ground up, greenfield projects and have come on second stage to large messy, pre-existing, legacy.

    I think what makes a great Ruby programmer is being able to navigate the legacy systems and fine tune them using proper Ruby standards. I’ve seen PHP developers, designers and even engineers of other sciences and industries think they can program because of the barriers Ruby tears down. I love having a diverse eco-system but these guys are what always lead to the terrible, copy pasta code bases.

    The same can be said in reverse, where someone that knows what they are doing can have an inexperienced, second stage team come in after them and destroy what they’ve left. I’ve seen it happen with a lot of the work I’ve left behind. It’s sad to see noobs destroy what they couldn’t support.

    1. Sandi Metz gave a talk at GoRuCo 2011 entitled Less that talked about writing exceptional code. She defined exceptional as: code that, when someone else comes in to modify, reuse, or build upon, forces them to do the right thing. IE: a noob who follows your pattern will be doing what’s good for the code base.

      I would suggest that if you find others ‘destroying’ your ‘good’ code all the time, you look at the patterns you left them to follow, and why they didn’t follow them ‘correctly’.

      I had this problem for a while, ‘why is everyone mucking with my good code?’ I didn’t learn anything from that, until I took the onus on myself to leave better patterns to follow.

      1. You are putting way too much faith in others being capable of following any pattern whatsoever. Half of them don’t even know what a pattern is.

  8. Part of it is the start-up culture and start-up business models with which Rails projects are often closely linked and for which Rails appears to be optimised. Rapid prototyping, Minimum Viable Product, etc. Often acquiring technical debt is the best choice for an early-stage start-up, much as it pains those who later need to grow or maintain the codebase.

    Not that this excuses poor planning and naivety in the way in which one acquires technical debt. Nevertheless it might partly explain why you’re seeing it so much of it on Rails projects…

  9. How much do you think testing plays a part in this? Have you found projects which come with tests to generally be cleaner?
    The Ruby and Rails community has adopted TDD more than most other communities I know. I would hope this would lead to cleaner code.

    Thanks for the writeup, I haven’t had a chance to see much Rails code in the wild outside of open source so it is good to hear from someone who has.

    1. Cleaner? Sometimes.  More code?  That too.

      This is my one problem with Ruby’s testing culture (and I say this as a “Rubyist”): tests are code too.

      Code is a liability.

      1. “Code is a liability”. If that is the case for your code, I hope you’ll never write code for me.

        Maintained code, developed to last an intended time-span which solves a business problem efficiently and effectively is always an asset. 

        Of course, over time this code loses value and monitoring and preventing these expenses is important. Unit-tests allows us to see the loss in value and correct it where necessary. As such, a well written test is an asset. It solves a business problem effectively by reducing the expenses made on existing software (thus creating a higher efficiency).

        1. Hrm. Rude.

          Test code requires maintenance like any other code.  The more code that you have, tests or otherwise, generally, the more effort it takes to maintain it.  I’m sorry if you can’t see that perspective.

    2. Maybe marginally cleaner.  There are a lot of issues which tests ought to force coders to address. But in Ruby a really determined developer can go to amazing lengths to create a test suite that accommodates crappy, highly-coupled code.

      This is why I hammer so hard on TDD being a design discipline, not a code coverage game. When you TDD just for coverage and ignore the signals the tests are sending you about your design, a lot of times you leave behind a test suite that has to be thrown away in order to move forward.

  10. Extremely well put. I have seen what you’re describing here first-hand, in my own experience on both sides of the table. There are two  different “sects” in the rails world (I’m simplifying, of course) — there are consultants who mostly do greenfield work (mostly for solid business reasons) and there are product programmers (and a few mad consultants who actually CHOOSE to take on legacy projects). The product programmers are people who are full-time at a company and are tasked with maturing a project past the initial few months of development. Sometimes this is an inherited role, sometimes the team members have been there from the start.
    My impression is that many of those who are so dismissive of solid practices established over the last couple of decades (gasp, they might have been used in Java) have not had the pleasure of being on a team that has to deal with real legacy problems. Also, Rails is trying to find a happy medium in maintaining it’s status as a “get stuff done fast” framework, and a framework suitable for larger, enterprise projects.

    Personally, I’m pretty happy with how things are shaking out. Thanks for the great post.

  11. Avdi,
      Thanks for saying this! I’ve too often had folks saying to me ‘oh that’s a Ruby pattern, that doesn’t apply to my language’. Good, maintainable OO design patterns are applicable to all OO languages. Well said.
    Thank You!
    **R

  12. Is this a software problem or a business problem?

    You create the most business value by taking on difficult projects and rescue missions that few others could or would choose to do.

    That may be more of the reason behind finding yourself in hellish code than the language, framework, or developers that code was written in/by.

  13. Is this a software problem or a business problem?

    You create the most business value by taking on difficult projects and rescue missions that few others could or would choose to do.

    That may be more of the reason behind finding yourself in hellish code than the language, framework, or developers that code was written in/by.

    1. I actually don’t think it’s our job to be concerned with business value. We should let the business decide that. Our job is to give them the value they want.

  14. Coming to ruby from java – these problems are prevalent there too.  After years of touting GoF and other OO related knowledge bases to people who don’t seem to “get it”, I’m starting to realize that the tools and languages we use, ruby included, don’t get it (really, the principles we’ve learned over time to best utilize OO do not scale to everyone that types code).  There will always be far more people typing than there are people who care what happens more than one release/build down the road.  As long as you can get away with doing something it will be done, and if that something is perceived to be extra – that is the problem.  Like you say the pattern repeats itself because it’s a problem matching people with tools.  The answer isn’t to have less people typing (I actually subscribed to this theory at one point) but maybe it’s a different approach that requires these things of goodness and not just suggests them for the subset that cares about them.  My two cents.

    1. Before I got my first Rails job, I was under the misapprehension that Ruby/Rails developers were universally fanatic about good practices (TATFT, anyone?). At that job I encountered a mire of bad code, technical debt and lazy culture. There are Ruby/Rails-focused organizations where “The Right Thing” is the norm, but they are just as rare as in any other programming community.

    2. Before I got my first Rails job, I was under the misapprehension that Ruby/Rails developers were universally fanatic about good practices (TATFT, anyone?). At that job I encountered a mire of bad code, technical debt and lazy culture. There are Ruby/Rails-focused organizations where “The Right Thing” is the norm, but they are just as rare as in any other programming community.

    3. I blame the education system for developers. Shockingly few universities and colleges deal with the “why” of programming, only the “how”, and almost all seem more focused on the tuition money coming in rather than the quality of education going out. The only programmers worth a damn anymore are the ones who taught themselves from youth, and we’re outnumbered by the 9-to-5ers churned out by higher education.

    4. I blame the education system for developers. Shockingly few universities and colleges deal with the “why” of programming, only the “how”, and almost all seem more focused on the tuition money coming in rather than the quality of education going out. The only programmers worth a damn anymore are the ones who taught themselves from youth, and we’re outnumbered by the 9-to-5ers churned out by higher education.

    5. I’m reading this as “Ruby is to the back-end what jQuery is to the front-end” – they lower the barrier for entry so that too many low-level developers can “write code easily” but not “write code well”

      1. jQuery, IMHO is breaking all the good practices and advocates this is better. I’m shocked that serious developers can’t get it, that javascript programming is no different. The good practices should be applied there, too.

        I’ve read one good slide for “Performance tips on jQuery”, where the author basically tells to write code the same way it is written and practice in more modular (better in my opinion) js frameworks

        1. Evgeni – I slightly agree with you. jQuery makes JavaScript “easy” but unfortunately, it made it so easy any idiot can (and does) try to tout themselves as an expert because they write code that may work in their browser of choice (and occasional IE6). Good practices should be applied to jQuery.

          I suppose the old adage “what is right isn’t always popular, what is popular isn’t always right” applies – I’m still a bigger fan of MooTools/Dojo over jQuery, but sadly jQuery “developers” are often who I’m handing code over to, so I can’t exactly force clients to “be better developers”

        2. Evgeni – I slightly agree with you. jQuery makes JavaScript “easy” but unfortunately, it made it so easy any idiot can (and does) try to tout themselves as an expert because they write code that may work in their browser of choice (and occasional IE6). Good practices should be applied to jQuery.

          I suppose the old adage “what is right isn’t always popular, what is popular isn’t always right” applies – I’m still a bigger fan of MooTools/Dojo over jQuery, but sadly jQuery “developers” are often who I’m handing code over to, so I can’t exactly force clients to “be better developers”

        3. In my experience, most developers don’t treat JS like a programming language at all. It’s ‘gloss and shine’, not code. When just starting, it’s fine to just use a couple of slideUp() and slideDown() calls. Then, like all things, it grows and builds – usually by a developer who doesn’t know what he’s doing or doesn’t respect JS as a language.

          The open nature of the web makes JS a copy/paste language. At least your server-side code, whatever it may be, is hidden from the user. Sad, really.

        4. What good practices does jQuery break? It’s just a function that spits out adapter/decorator objects for normalizing and reducing cruft in the DOM API. IMO, a very well-written one that you could a learn a lot about JavaScript from. But it mostly just covers the DOM. It doesn’t architect apps for you.

  15. Nice, great to hear the perspective of a Rails developer, I myself am a Java developer that moved to Groovy and onto Grails a few years ago.  The funny thing is, when people move to expressive languages, myself included, people find more ways to express themselves badly.  Just because it is simple to do things “right” the discipline drops and then we are back to a kind of code that people wrote with perl to reinvent the wheel.

    People abuse freedom in everyday life, software developers are no different. 🙂

  16. “Rails developers are sometimes accused of being arrogant
    and judgmental. I’m not sure how true this is; I don’t see it all that
    much..”

    “What I do see is a kind of “Rails exceptionalism”. ”

    I found these two statements so close together very amusing and think that perhaps it betrays a lack of self-awareness. It is exactly the belief in a “Rails execptionalism” that makes anyone associated with Rails seems arrogant and judgmental if you work on another platform.  After all they believe that they use a magical platform that makes them immune to any of the problems that you are having and you are obviously just not good enough to figure that out (none of which of course is true).

  17. Personally, I think there are several things to blame for this problem. The first are the developers creating these large projects. I have worked on two rather large projects, my current one, including tests, is 300+ KLOC (140+KLOC without). 

    For the current one, culprit is that it was started by Java developers who were tired of all the boiler plate code needed to get J2EE applications up and running. They were also tired of fragile tests due to mocks. As such, when they came to rails, they embraced the freedom to move quickly and also made the decision to not use the features from Java that caused them pain, e.g., mocks, stubs, isolated unit tests, etc. Today, this means we need to use a distributed test framework in order to get our tests to run in under 2 minutes (they take almost 2 hours otherwise).

    The other project was started by a group of php programmers who had to experience at all in developing software. Thus, what they created was a mix of bad programming and and a need to use all the latest gems and “shinies” that were released. They also had no idea how to evolve software or do TDD properly to drive to the design. Design patterns were not even on these guys radar, nor could they have understood them. They simply weren’t mature enough in their skills and understanding of software development.

    Overall, I think the biggest factor here is speed. Rails allows you to move fast and that speed is addictive. Whether you are a new programmer or a seasoned software developer, the speed causes problems. As you said, Avdi, people get used to being able to quickly band-aid up solutions and they never really think about the long term consequences of what they are doing.

    In addition, on the subject of speed, all rails applications start as greenfield development projects. They are either spikes or start-up apps that end up going into production. During this phase, most people do not think about patterns and SOLID principles. They simply need to get a concept put together and in front of people. The debt can wait. Whereas, Java, .NET, C++ projects are understood to be enterprise applications that need to last. As such, good development teams will ensure they have experienced developers on board and will take more time to think things through.

    The last contributor that I need to mention is an attitude in the Rails/Ruby community that Ruby is the final language. I am not sure that this is an attitude of superiority or just a general hatred of other languages (i.e., Java), but the end result is that every problem is solved in Ruby/Rails. They are very reluctant to explore other languages. Ruby is a very expressive language, but it has its limitations and most of the Ruby developers I have worked with are not willing to acknowledge this. They are willing to spend days or weeks optimizing a long running query or heavy lifting process instead of implementing it in Java or C. They create very large, monolithic applications that do everything instead of breaking them up into smaller services or background jobs created using the right language for the problem.

    I am not sure how to solve any of these problems. I am currently trying to figure out how to bring mocks and stubs into my work environment in order to create true unit tests and speed up our tests, but at over 140+KLOC of test code, it is a very tough battle. Especially since the lead devs are unwilling to make the change. Hopefully, as the community matures and Rails apps get bigger, this will work itself out.

  18. Nice article. I used to do the same with large-scale Java projects, usually being brought in at the point where they were creaking at the seams due to unrefactored code, lack of tests, poor design, etc. and so on. Thankfully I gave it up and now build a software product so the only code I have to live with is our own.

    One of the things that strikes me about Ruby/Rails as well as a lot of functional programming languages is that small does not necessarily equal beautiful and rarely equals simple to understand. One thing to be said about those Java projects is that it was never really that hard to work out what was going on due to static bindings and verbose style.

    Which isn’t to say that Ruby/Rails is somehow worse than Java; I’ve recently grown to love it and hope I don’t have to go back. The point is, as you say,  irrespective of language, lack of a good process or approach and lack of good discipline can bollocks up any project.

  19. Nice article. I used to do the same with large-scale Java projects, usually being brought in at the point where they were creaking at the seams due to unrefactored code, lack of tests, poor design, etc. and so on. Thankfully I gave it up and now build a software product so the only code I have to live with is our own.

    One of the things that strikes me about Ruby/Rails as well as a lot of functional programming languages is that small does not necessarily equal beautiful and rarely equals simple to understand. One thing to be said about those Java projects is that it was never really that hard to work out what was going on due to static bindings and verbose style.

    Which isn’t to say that Ruby/Rails is somehow worse than Java; I’ve recently grown to love it and hope I don’t have to go back. The point is, as you say,  irrespective of language, lack of a good process or approach and lack of good discipline can bollocks up any project.

  20. I can dig it.  I wonder what we can do about this.  

    The Ruby and Rails communities seem pretty good about putting out great training materials, howtos, tutorials etc…  Maybe we should try to get some momentum going to move people from “I can make a blog in 15 minutes” to “I understand some useful design patterns”?

  21. I can dig it.  I wonder what we can do about this.  

    The Ruby and Rails communities seem pretty good about putting out great training materials, howtos, tutorials etc…  Maybe we should try to get some momentum going to move people from “I can make a blog in 15 minutes” to “I understand some useful design patterns”?

  22. For greenfield projects, you can use Rails to double programmer productivity. Is this worth the cost of back-end code maintenance?

    Yes. Here’s why. 90% of all startups fail, basically because the product did not fit the market or because business execution was not good.  Now suppose you have 10 projects with the same number of units of functionality. Optimistically, you would expect perhaps 6 failures before a success. (Pessimistically, 13).  Suppose it costs $100k to develop each one in 3 months using Java. With Ruby/Rails, you can spend $100k and it’ll be done in 6 weeks. Let’s now suppose (very unrealistically) that it takes 3 months and $100k off biz-dev to figure out if the product can profitably serve a market. That means that with Ruby/Rails it costs 4.5 months to figure it out, and with Java, it costs 6 months. That means that to get to a successful product with Rails (8th try) it’ll cost you $1.6 million of dev/biz-dev and 36 months. With Java, it will be $1.6 million of dev/bizdev  and 48 months. 

    So, then, what is the opportunity cost of a year of missed sales with a good product  that fits its market? It is a lot. Yes, the Ruby guys will spend a pantload of money hiring people like you to clean up the code, but that is a lot more palatable to investors between the 3rd and 4th year when sales and profits are coming in, rather than in the first 3 years (using great up-front practices) when there are no meaningful sales or because they don’t know if the product fits the market. 

    On average, you are getting hired into the 10% of projects which succeed, and now need a code cleanup to be able to serve the market more profitably. Their code may be a spaghetti ball, but it is a successful spaghetti ball which enables a successful business. And a successful spaghetti ball has much more value than a well-architected well-encapsulated, design-patterned, extensible code base which nobody  uses, and enables nobody to make money. 

    On average, the failed Rails projects fail much more cheaply than failed Java projects, both in development costs and opportunity costs of delivering a system late to market (e.g. Java, in this example has an opportunity cost of 6 weeks times the number of sales or feedback units).  Failing fast lets both the devleopment team and the business development team develop a deeper domain expertise in the product, the market, and the businesses processes enabled by the product to serve the market. And that will only lead to more success faster. 

  23. IMO the issue is that all the whiz-bang Rails tutorials just show how to use scaffolding and throw stuff together, maybe with some trivial testing.  This is where Rails got the reputation for not caring about design patterns or things like that; everything talking about Rails talks about how you DON’T need that stuff.  Hell, the Rails conventions themselves even talk against good practices like enforcing constraints at the database level – the database is just a “storage container” and all logic should be in the model.

    It’s not Rails, its the attitude of people who use it who don’t care about doing things right but want to crank it out fast.  I come from the C#/.NET world and that’s 99% of what I see on a daily basis, and have seen for six years working in C# – hacks who think refactoring, unit tests, SOLID, design patterns etc. are all “time wasters” and prefer to just slop code in a code-behind file somewhere or write a stored procedure and call it directly with a SqlCommand.

  24. I just loved reading this! It’s incredibly true and I think in many ways it’s easier for someone multiplatform like me to come to ruby and rails because I learned about this the hard way. I am just so happy with ruby because writing good OO code is so darn easy. I can really express my intent in just a couple of lines of code what would be so much harder to read in C#. unless anyone?

    Hope I never have to inherit technical debt like that…

  25. The strange thing is, there are plenty of great books on how to write good code in ruby.  The design patterns one, Eloquent Ruby, Refactoring: Ruby Edition, Rails Antipatterns, The RSpec Book.  It’s just that most people are too lazy to read them or actually apply the concepts.

  26. Would things be different if not so many people Ruby developers were using it to develop Rails applications, but were working on longer-term non-Rails projects? People’s behaviour gets affected by their peers, so it wouldn’t just be the people working on the 10-year project that’d be different, but the people who go to the same meetups as them.

  27. Would things be different if not so many people Ruby developers were using it to develop Rails applications, but were working on longer-term non-Rails projects? People’s behaviour gets affected by their peers, so it wouldn’t just be the people working on the 10-year project that’d be different, but the people who go to the same meetups as them.

  28. One of the best pieces of advice I ever had, and have kept it in mind ever since is similar to the last paragraph of this post, and could not be more on the money:

    “Write your code as if the next person to work on it is a violent psychopath with anger management issues who knows where you live.”

  29. Rails promises rapid results. Rapid results are attractive to people with short term priorities. In my experience in Chicago it’s the popular choice for startups writing prototypes and marketing/interactive agencies writing throwaway apps. I don’t personally like rails but I don’t blame the technology so much as management for failing to understand how much value is lost when no effort is put on hiring experienced devs who can write maintainable code. For greenfield work it really shouldn’t add that much time.

Leave a Reply to Avdi Grimm Cancel reply

Your email address will not be published. Required fields are marked *