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.
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.
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...
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.
- 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.
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."
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.
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.
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.
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.
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.
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.
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.
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.
"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).
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.)
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.
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.
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.
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.
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.
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...
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. :)
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.