This post originated from an RSS feed registered with .NET Buzz
by Brad Wilson.
Original Post: A Look at How Languages Influence Development
Feed Title: The .NET Guy
Feed URL: /error.aspx?aspxerrorpath=/dotnetguy/Rss.aspx
Feed Description: A personal blog about technology in general, .NET in specific, and when all else fails, the real world.
It's no secret that I'm in love with dynamic languages in general, and with Ruby in particular. The performance question is one that always comes up early. Ironically, the people asking this question are usually coming from a managed environment like Java or C#, which were subjected to the same questions by C++ junkies. Talking about business apps in general, and web apps in particular, Dave really nails this:
Let's face it: your average commercial application isn't burning CPU cycles solving NP-complete problems. We typically write code that moves chunks of data about and adds up a couple of numbers. In these scenarios, is it worth worrying about the relative performance of the language used to do the moving and adding? Not in my book.
Dave reiterates something that I have always found to be true: one of the major performance bottlenecks under a developer's control is choosing good vs. bad algorithms, not fast vs. slow computer languages. If you implement an O(n^2) algorithm in C++ and I implement an O(n) algorithm in Ruby, I'm going to kick your ass for any significant number "n". Scaling is generally a matter of minimizing your poor performance algorithms.
Here's the point Dave makes:
Iâd rather write in a language that letâs me focus on the application, and which lets me express myself clearly and effectively. And, if I can do those two things, I believe that sometimes Iâll be able to write code which is cleverer than something Iâd write in a lower-level language. A better algorithm will easily gain back any marginal performance hit I take for using a slower language.
Justin Ghetland experienced this recently on a Rails project. Having coded the same application twice, once in Java and once using Ruby on Rails, he was surprised to discover that the Rails application outperformed the Java one. Why? Justin believes itâs because Rails does smarter caching. The Rails framework contains some very high-level abstractions, and that allows the folks writing the framework to be smart about what they do. They accepted the linear hit of writing in a language that executes more slowly because they got a non-linear increase in speed from being able to write better code.
Let's just say that I think he's right on here. So let's talk about something Dave hasn't mentioned yet. His blog post is described as the first in a series, so maybe he's going to get here, with more eloquence and fewer words than me. :)
Databases are probably the primary place where application scalability tends to fall down. I find it interesting that the prevailing philosophy (in the .NET community, anyway) is that you should do as much work as possible in the SQL layer. I tend towards the opposite: nothing but simple select, insert and update statements, and never in stored procedures. I don't want lots of business logic enforcement in my database, in a language that's not the same as the application, with generally higher maintenance costs than the application itself. I'd rather use the database for the task at which it excels: locate and limit the data which I'm interested in, and get it back to me as soon as possible. Scaling wide with web servers is cheap and easy, but your database servers are big and expensive.
One solution that people tend to evaluate, to help them manage this burder, is object/relational (O/R) mapping tools. When using a static typed language, you generally ends up with O/R tools which scour databases, then generate a heapload of classes (in source code form) based on the shape of your database tables.
With this style of O/R comes disadvantages. First, the view of the database is made at a moment in time. It doesn't easily adapt itself to changing database schemas (and, worse, completely fails when the database schema is dynamic and not known at application development time). When a database is only used for a single application, this burden can be minimized, but once databases start to get shared across apps, then keeping everybody revisioned can become a major integration challenge.
The more insidious disadvantage, though, is what it does to a developer's brain. When you have this static view of the database, expressed in terms of n classes to match your n tables/views, then you tend to solve your problems in those precise terms, because the code generated by the O/R code generation tools will encourage (and perhaps even enforce) such behavior.
ActiveRecord, the O/R mapping layer used predominantly by Ruby on Rails applications, has a different world view. It lets you think in the "n classes for n tables" way, but it does the work at runtime. ActiveRecord takes full advantage of Ruby's powerful meta-programming model. It can take a result set of data, and dynamically model a class based on it. If the select statement you sent up was something simple like "select * from tablename", all the columns come back, and they'll all be available as accessors. But if your optimized query is just for 2 of the 17 columns in your table, then the objects that come back have 2 accessors only.
In fact, the use of "model" classes at all in ActiveRecord is just a convenience facade for you. Using ActiveRecord to model a table named "Users", you might write:
class User < ActiveRecord::Base
end
(There is occasionally more to it than that, but this is a good starting point). At first glance this might look like you're doing something interesting with class identity and mapping it to table identity somehow, but remember that Ruby is a dynamically typed language with "duck typing". My parameters to methods don't have type, so there is no value to having an "identity" for your database classes. What's really happening is that you're asking ActiveRecord for a nice facade around a table (which gives you a place to talk about inter-table relationships, like foreign key relationships). The name of the class is merely instruction to ActiveRecord about what the (default) table name will be, so that you can write interesting code like:
class Book < ActiveRecord::Base
def self.findByISBN(isbn)
find (:first,
:conditions => ["isbn_code = ?", isbn])
end
def self.specials
find (:all,
:conditions => "is_discount = 1",
:order => "price",
:limit => 10)
end
end
// Then calling from your code...
book = Book.findByISBN('18509483')
books = Book.specials
without worrying about table names (or even actual SQL).
The reality is that you can use complex queries with joins which create entirely transient views of data, and it'll happily meta-create an object instance (or collection of such instances) to describe the result(s) you got back, whether it's even remotely related to tables or views in your database.
This kind of power and flexibility comes with a runtime cost, of course. But it comes with tremendous developer value, too. You don't feel bound by some code generation tool's static view of what your data "should" look like, based on table definitions. It gets the grunge out of your way, so you can think about the best way to solve your specific problems.
How many database scalability issues do you suppose can be traced back to trying to fit an odd query into the static class hierarchy of an O/R tool? How many times do you suppose people evaluate O/R mapping tools, and find them unsuitable, precisely because they take such a static, moment-in-time view of your data? How many of those people were really looking for something like ActiveRecord, but unknowingly let down by the fact that their choice of a statically typed language made such things very difficult?
I'm not saying that Ruby or ActiveRecord are the best solutions here. I merely use them as the example I know best. I think I'll leave for another day a discussion of what I see as the inevitable divergence of static vs. dynamic typing. Languages like Python and Ruby are more socially acceptable now, in ways that Smalltalk and Lisp never quite managed.
I have high hopes that my employer will see and understand why people are so psyched about dynamic languages, and give them a first class place to play with the static typed people on Windows.