There is nothing new except what is forgotten
Isn’t it fun that we had “blog in 15 minutes” video for many years now, but industry standard blog engine is written in “fugly” PHP?
There are always interesting things happening in Ruby and Rails ecosystem. Our community embraces change, even when change means redefining conventional wisdom.
alias_method_chain has stopped being considered a good practice, and it was only beginning. Today we refer to classics of SOLID design principles, while people continue revisiting patterns and practices that have made Ruby stand out as highly dynamic runtime. Take
method_missing as another example: it can be harmful just like any other powerful technique. What’s next? Dynamic dependency loading? (It’s evil indeed)
I can’t help myself but
rant play with some fundamentals too, so I’d like to focus on OO design and SOLID theme in particular. I don’t think that all five principles are equally applicable to Ruby because of dynamic typing. SRP (Single Responsibility Principle) may be the only one that can be taken as it is. OCP (Open Closed Principle) makes sense as well, but given Ruby open classes, needs an updated statement (i.e. what is meant by modification and extension).
LSP (Liskov Substitution Principle) is much more curious to think of in Ruby. Why would anyone want to consider such boring thing as this:
Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
when Ruby offers such wonderful and fun version as Duck Typing:
When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.
I’m being sarcastic here, of course I don’t think that Duck Typing can substitute LSP. Have a look at behavioral conditions section in the wikipedia article for reasons. The fact that LSP is attracting attention right now can be pretty telling. I’m excited to witness it. Community is finally seeking for some grownup approaches, great! However, the question is: doesn’t that get us closer to some limitations of our beloved highly dynamic language?
Rails code is rapidly evolving, the same is true for the community and ideas behind it. The fact that such static typing rituals as DIP (Dependency Inversion Principle) and ISP (Interface Segregation Principle) start being considered may mean that within the current pace of growth, it becomes more and more of a challenge to manage complexity. It is well known that in order to successfully “Duck Type” one’s way through code, he or she should often mind more details.
Rituals are what we hate in Java and C++, it’s what gets in our way when we want to take the shortest path from an idea to it’s expression. But as codebase becomes bigger and more complex, rituals become inevitable. I think everyone who has got past “15 minutes blog app” state and the euphoria of “I’ll put together a dozen of gems and my CMS is done, than add some more and here is my CRM” knows that in order to build (and scale) apps beyond the framework, some set of carefully picked rituals is very important. Often the very same set that was in the way at the beginning. An extreme example of building beyond framework is Twitter and their reasons behind switching to Scala.
The fact that you can call any private method you like, or access any instance variable, or reopen class and do whatever you want is fun. But doesn’t that lead to less time spent on thinking about fundamentals like encapsulation? How often do we have thoughts like “should this method that I write be public, private or protected” ?
Ruby is famous for its well thought OO paradigm. It has been influenced by Smalltalk design, which seems to mean a lot in the world of OO. The reality however is that de-facto standard
attr_accessor doesn’t contribute at all to good design. And even if we put some opinionated decision and use
attr_reader instead, doesn’t that still feel like we’re pretending encapsulation? For example if we expose an array trough
attr_reader :x, we can’t do direct assignment, but what about
x elements? Being able to ensure immutability on language level is good for VM performance optimizations and very important for various concurrency patterns. There are plenty of opportunities to break encapsulation. Of course being able to do something doesn’t necessarily mean having to do that, which brings us to certain aspects of code discipline. But, isn’t it all about rituals again? Implicit rituals unfortunately.
I heard a phrase at Railsconf 2011, that we end up writing more tests because of interface specs (Lint), and it’s ok to do so. It may be true in that particular case, but generally, is it okay to write more code especially when it does the work that interpreter or runtime can do better?