<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;Ak4MQHw8cSp7ImA9WhBUEUQ.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497</id><updated>2013-04-28T18:29:41.279-07:00</updated><title>You work for me, Computer</title><subtitle type="html" /><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://blog.brandonbloom.name/" /><link rel="next" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default?start-index=26&amp;max-results=25&amp;redirect=false&amp;v=2" /><author><name>Brandon Bloom</name><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh3.googleusercontent.com/-w9OBZ4t1-dc/AAAAAAAAAAI/AAAAAAAAAB0/of9MWhArRcM/s512-c/photo.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>47</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/YouWorkForMeComputer" /><feedburner:info uri="youworkformecomputer" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;AkEBSHwzcCp7ImA9WhBTFE8.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7877317490240840074</id><published>2013-02-09T08:50:00.004-08:00</published><updated>2013-02-09T08:50:59.288-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-02-09T08:50:59.288-08:00</app:edited><title>Personal Update: NYC</title><content type="html">I've moved to New York City! The plan is to bootstrap my next company, so stay tuned for a lot more details about that in the near future. Meanwhile, I'll be available for contract work, so shoot me an email at brandon@brandonbloom.name if you're in NYC and would like to chat.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/XPHODH4sOWI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7877317490240840074/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7877317490240840074" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7877317490240840074?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7877317490240840074?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/XPHODH4sOWI/personal-update-nyc.html" title="Personal Update: NYC" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2013/02/personal-update-nyc.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEEFQHk6cCp7ImA9WhNUGUg.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7117638519321616660</id><published>2013-01-11T18:10:00.001-08:00</published><updated>2013-01-11T18:10:11.718-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-11T18:10:11.718-08:00</app:edited><title>Fipp: Fast Idiomatic Pretty-Printer for Clojure</title><content type="html">&lt;br /&gt;
I decided to create a new pretty printer in the spirit of "Data All The Things!" And it's fast too!&lt;br /&gt;
&lt;br /&gt;
&lt;a href="https://github.com/brandonbloom/fipp"&gt;Fipp&lt;/a&gt;, the Fast Idiomatic Pretty-Printer for Clojure, is a pretty printer with linear runtime and bounded space requirements. Unlike clojure.pprint's side-effectual API, Fipp is configured by pretty print documents that are similar to Hiccup HTML templates. It's based on some fancy pants research pretty printers from the Haskell world.&lt;br /&gt;
&lt;br /&gt;
You can see my implementation and lots more notes at &lt;a href="https://github.com/brandonbloom/fipp"&gt;https://github.com/brandonbloom/fipp&lt;/a&gt;&lt;br /&gt;
&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/smtxo4Wou9A" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7117638519321616660/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7117638519321616660" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7117638519321616660?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7117638519321616660?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/smtxo4Wou9A/fipp-fast-idiomatic-pretty-printer-for.html" title="Fipp: Fast Idiomatic Pretty-Printer for Clojure" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2013/01/fipp-fast-idiomatic-pretty-printer-for.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEMCR3o9eSp7ImA9WhNUEEQ.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-5895213792276543989</id><published>2013-01-01T18:07:00.002-08:00</published><updated>2013-01-01T18:07:46.461-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-01T18:07:46.461-08:00</app:edited><title>Factjor: Concatenative Clojure</title><content type="html">I've released another small Clojure project: &lt;a href="https://github.com/brandonbloom/factjor"&gt;Factjor&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Head on over to GitHub for a tiny intro in the README. Lots more&amp;nbsp;explanation&amp;nbsp;and context will follow in a future post, after I release the project that motivated Factjor.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/8DcKLWwo3GE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/5895213792276543989/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=5895213792276543989" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/5895213792276543989?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/5895213792276543989?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/8DcKLWwo3GE/factjor-concatenative-clojure.html" title="Factjor: Concatenative Clojure" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2013/01/factjor-concatenative-clojure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUYFRn86fyp7ImA9WhNVEEo.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-9053252177499307315</id><published>2012-12-21T00:05:00.000-08:00</published><updated>2012-12-21T00:05:17.117-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-12-21T00:05:17.117-08:00</app:edited><title>The Node.js REPL is Broken</title><content type="html">Not only is the Node.js REPL broken, but so are the stock REPLs for Python and Ruby too!&amp;nbsp;In fact, for many of you, every single REPL you've ever used has been fundamentally flawed. Moreover, even if you used a fancy replacement REPL, such as &lt;a href="https://github.com/Benvie/Node.js-Ultra-REPL"&gt;UltraREPL&lt;/a&gt;, &lt;a href="http://ipython.org/"&gt;IPython&lt;/a&gt;, or &lt;a href="http://pryrepl.org/"&gt;Pry&lt;/a&gt;, you'd only be mitigating the problem: JavaScript, Python, and Ruby are all fundamentally flawed languages from a REPL-lover's perspective.&lt;br /&gt;
&lt;br /&gt;
Now, I realize that this is a bold and, thus far, unsubstantiated claim. Python and Ruby are revered for interactive, dynamic development and Firebug's JavaScript console changed the game for web apps. So with so many happy REPL users, what are these fundamental flaws? Since it's the&amp;nbsp;worst offender, let me pick on JavaScript for a moment...&lt;br /&gt;
&lt;br /&gt;
Node's stock REPL provides a fragile and awkward notion of an evaluation context. A fresh REPL is configured with an evaluation context as if by both &lt;i&gt;with(context)&lt;/i&gt;&amp;nbsp;and &lt;i&gt;.call(context)&lt;/i&gt;&amp;nbsp;to enable unqualified access to context variables, as well as introspection of the&amp;nbsp;&lt;i&gt;this&lt;/i&gt;&amp;nbsp;context. This, however, is a&amp;nbsp;convenient&amp;nbsp;illusion. If you evaluate &lt;i&gt;var x = 1; this&lt;/i&gt;, you'll note that &lt;i&gt;x&lt;/i&gt;&amp;nbsp;is added to &lt;i&gt;this&lt;/i&gt;. If you were to use &lt;i&gt;var&lt;/i&gt;&amp;nbsp;inside a &lt;i&gt;with&lt;/i&gt;&amp;nbsp;block, you wouldn't get that same behavior. The top-level inside the REPL is somewhat magical.&lt;br /&gt;
&lt;br /&gt;
CommonJS Modules provides the blueprint for Node's module system. The key idea is that Plain Old JavaScript Objects can be used to describe modules and their exports. Simple, but perhaps too simple. Consider if you were working on some function &lt;i&gt;f&lt;/i&gt;&amp;nbsp;in module &lt;i&gt;m&lt;/i&gt;. You boot up your trusty REPL, evaluate&amp;nbsp;&lt;i&gt;var f = require('m').f;&lt;/i&gt; and try out some corner cases. Ugh oh, &lt;i&gt;f&lt;/i&gt;&amp;nbsp;has a bug, so you modify &lt;i&gt;src/m.js&lt;/i&gt;&amp;nbsp;and then you.... promptly restart your REPL! The &lt;i&gt;require&lt;/i&gt;&amp;nbsp;function doesn't offer any sort of &lt;i&gt;{reload: true}&lt;/i&gt;&amp;nbsp;option, and even if you did, you'd still be left with a reference &lt;i&gt;f&lt;/i&gt;&amp;nbsp;pointing to an old version of the function. That might not be a big problem if you have just one function, but what if you have several objects and functions in a larger system? The too-simple plain old JavaScript objects approach results in the copying of references and assigning those copies to local names. It's extremely difficult to reason about changes. Even if you knew to cheat by reaching inside of &lt;i&gt;require.cache&lt;/i&gt; with a &lt;i&gt;delete&lt;/i&gt;, you'd still be better off restarting your REPL to preserve your sanity. That's a real shame if you had a bunch of interesting test cases lying around in memory, or if there is an expensive initialization computation&amp;nbsp;you need to wait through each time.&lt;br /&gt;
&lt;br /&gt;
Now that I'm done picking on JavaScript, I'll point out that the situations in both Python and Ruby aren't much better. Python doesn't have a notion of a current module, although it does provide the builtin&amp;nbsp;&lt;i&gt;locals()&lt;/i&gt;&amp;nbsp;function, as well as lets you call&amp;nbsp;&lt;i&gt;code.interact(locals={...})&lt;/i&gt;&amp;nbsp;to start a nested sub-REPL with something akin to Node's magical top-level. Ruby fairs marginally better by providing a reasonably well behaved implicit &lt;i&gt;self&lt;/i&gt;, which can be shadowed in a nested sub-REPL via an argument to the confusingly named &lt;i&gt;irb&lt;/i&gt; method. Python's modules have a similar issue to plain old JavaScript objects with &lt;i&gt;from m import f&lt;/i&gt;&amp;nbsp;and Ruby's metaprogramming facilities add more than enough complexity to make REPL-coding hair-raising.&lt;br /&gt;
&lt;br /&gt;
It doesn't have to be this way. There are languages that are much better suited to iterative REPL development. If you've been following my blog, then this is where you'd expect me to praise Clojure. I'll save that for a future post in which I'll discuss what is really necessary for a successful REPL. And while Clojure's REPL is far better than any other I've ever used, it's got it's own shortcomings to discuss as well.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/nPUlVqIKvh8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/9053252177499307315/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=9053252177499307315" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/9053252177499307315?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/9053252177499307315?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/nPUlVqIKvh8/the-nodejs-repl-is-broken.html" title="The Node.js REPL is Broken" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>3</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/12/the-nodejs-repl-is-broken.html</feedburner:origLink></entry><entry gd:etag="W/&quot;AkUGQ3k_eSp7ImA9WhNUEkw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-3068856552734381045</id><published>2012-12-10T00:24:00.003-08:00</published><updated>2013-01-03T05:03:42.741-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2013-01-03T05:03:42.741-08:00</app:edited><title>Clojure's multimethod dispatch as a library</title><content type="html">&lt;br /&gt;
&lt;b&gt;[In keeping with the theme, this post motivates &lt;a href="https://github.com/brandonbloom/dispatch-map"&gt;dispatch-map&lt;/a&gt;]&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Clojure's multimethods embody a powerful polymorphic dispatch mechanism. With multimethods, you can define a function which is polymorphic with respect to a given hierarchy and arbitrary dispatch value. The hierarchy is actually a directed acyclic graph, but you can adjust method priority to address &lt;a href="http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem"&gt;the diamond problem&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, Clojure's dispatch mechanism is baked into the defmulti and defmethod forms. If you want multimethod-style dispatch, you need a Var to hold your multimethod. Similar to the circumstances which prompted the creation of &lt;a href="https://github.com/brandonbloom/backtick"&gt;backtick&lt;/a&gt;, I discovered that there was an opportunity to decomplect a part of the core library to extract a useful subcomponent. In this case, the datastructure which describes dispatch rules was complected with the identity which points to those dispatch rules. As a result, it's impossible to treat a multimethod as an immutible value.&lt;br /&gt;
&lt;br /&gt;
So I created &lt;a href="https://github.com/brandonbloom/dispatch-map"&gt;dispatch-map&lt;/a&gt;. A dispatch map is just like a regular Clojure map, but it comes coupled with a dispatch function and hierarchy. Looking up keys in a dispatch map has the same dispatch behavior and caching functionality of multimethods. However, unlike a multimethod, a dispatch-map is a true value. The standard map operators assoc, dissoc, etc, return a new dispatch-map, leaving the original unmodified. This enables you to manipulate dispatch maps like any other Clojure data structure, without the need for a named, mutable Var.&amp;nbsp;Clojure's multimethods and all related functions could trivially be reimplemented in terms of a dispatch-map and an atom &lt;span style="font-size: xx-small;"&gt;&lt;i&gt;(edit: and now are!)&lt;/i&gt;&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
One use for a dispatch-map as a value is to store them within some other data structure, without breaking the value&amp;nbsp;guarantees&amp;nbsp;of the enclosing data structure. In my case, I've got a dispatch-map of data types to their GUI representations. In a hierarchical definition of a GUI, each node can have a :templates key which is a &lt;i&gt;(dispatch-map class)&lt;/i&gt;. If you want to render a &lt;i&gt;(Person.)&lt;/i&gt; inside a list box, you can rely on automatic dispatch by type to find the appropriate template: Simply walk up the GUI hierarchy and look in the :templates map. Thanks to the powerful dispatch functionality, this will also work if you have an &lt;i&gt;(Employee.)&lt;/i&gt; too!&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/yBkbJLqgCxs" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/3068856552734381045/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=3068856552734381045" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3068856552734381045?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3068856552734381045?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/yBkbJLqgCxs/clojures-multimethod-dispatch-as-library.html" title="Clojure's multimethod dispatch as a library" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>5</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/12/clojures-multimethod-dispatch-as-library.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0IFR38zfyp7ImA9WhNXFEw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-1693448114096082091</id><published>2012-12-01T19:38:00.001-08:00</published><updated>2012-12-01T19:38:36.187-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-12-01T19:38:36.187-08:00</app:edited><title>Dataflow Solver (qplan for Clojure)</title><content type="html">&lt;b style="background-color: white; color: #222222; font-family: Arial, Tahoma, Helvetica, FreeSans, sans-serif; font-size: 13.600000381469727px; line-height: 14.399999618530273px;"&gt;[This post describes the motivation for my next Clojure library: &lt;a href="https://github.com/brandonbloom/qplan"&gt;qplan&lt;/a&gt;]&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
The&amp;nbsp;overwhelming&amp;nbsp;majority of graphical applications are built using ad-hoc event callbacks. Some portion of graphical applications, however, use a databinding model of varying expressivity. Often, these databinding models are simply shorthand for ad-hoc dependency graphs composed of event callbacks.&amp;nbsp;Inevitably,&amp;nbsp;the databinding system will be insufficiently expressive, necessitating manual callback wiring.&amp;nbsp;Even with the most expressive data binding systems available in application frameworks these days, there is a shockingly fragile rat's nest of control flow and execution order.&lt;br /&gt;
&lt;br /&gt;
Quite a bit of activity has occurred in the research community surrounding more formal, more composable, and less fragile event handling. The buzzword de jour is "&lt;a href="http://c2.com/cgi/wiki?FunctionalReactiveProgramming"&gt;Functional Reactive Programming&lt;/a&gt;" (FRP) which, although quite varied by author, generally involves the concepts of continuous "signals", discrete "events", and some evaluation strategy. FRP was born of animation systems and is a fertile research area. More generally, FRP is a type of &lt;a href="http://en.wikipedia.org/wiki/Dataflow_programming"&gt;dataflow programming&lt;/a&gt;. The "reactive"&amp;nbsp;moniker also applies to the area of push-sequences, such as .NET's &lt;a href="http://msdn.microsoft.com/en-us/library/dd990377.aspx"&gt;IObservable&lt;/a&gt; and associated &lt;a href="http://msdn.microsoft.com/en-us/data/gg577609.aspx"&gt;Reactive Extensions&lt;/a&gt; (Rx).&lt;br /&gt;
&lt;br /&gt;
As fun as it has been to study FRP, there has been little success building real applications using its techniques. In my opinion, this is because FRP encourages a continuous model of your application, but most graphical applications are much more discrete in nature. Multimedia systems, like the animation system that motivated the first FRP paper, and games have quite a few continuous use cases. Point-and-click software, however, generally has discrete controls, with discrete values, and discrete commands. Consider, for example, the undo-able discrete mutations in the most broadly deployed dataflow application on the planet: spreadsheets. Although capable of representing discreet events, FRP systems project the mental framework of water flowing through pipes. This mental framework clashes badly with the more familiar event callback model. Furthermore, even for truly continuous use cases, discretization is essential to the nature of computation; every game fundamentally distills down to an update and render loop.&lt;br /&gt;
&lt;br /&gt;
I'm still contemplating whether or not it is possible to build a databinding system that is an order of magnitude more expressive and composable than the existing state of the art. However, I'm certain that the underlying architecture will be a discrete system which embraces a formal notion of time through an explicit notion of &lt;a href="http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey"&gt;identity and value&lt;/a&gt;. Consider again the spreadsheet, where cells have an identity composed of their row and column labels. As you change the spreadsheet, the document updates the values in all cells synchronously in response to a discrete command. This approach can be extended to an arbitrary graph of identities connected by dataflow constraints.&lt;br /&gt;
&lt;br /&gt;
Given a sprawling graph of identities, you need some evaluation system for assigning values to each identity. Unlike a spreadsheet, many applications demand sophisticated and potentially cyclical bindings. In the face of multi-input, multi-output, and multi-directional dataflow, there are many potential evaluation strategies yielding many different graph linearizations. You need a constraint solver suited to the task. Luckily, Texas A&amp;amp;M has studied this problem in collaboration with Adobe. Their approach is referred to as &lt;a href="https://parasol.tamu.edu/~jarvi/papers/gpce08.pdf"&gt;Property Models&lt;/a&gt; and addresses many of the issues with relying on solvers for user-facing constraint systems. The Property Models approach is much broader than simply databinding, it also considers predictability and effectively&amp;nbsp;synthesizing&amp;nbsp;commands, such as those recorded in Photoshop's History pane.&lt;br /&gt;
&lt;br /&gt;
At the core of the Property Model system, is a variation of &lt;a href="http://www.cparity.com/projects/AcmClassification/samples/225543.pdf"&gt;Zanden's Incremental Algorithm for Satisfying Hierarchies of Multiway Dataflow Constraints&lt;/a&gt; as &lt;a href="https://parasol.tamu.edu/publications/download.php?file_id=670"&gt;described by Freeman, et al&lt;/a&gt;. I've produced a Clojure implementation of this algorithm called &lt;a href="https://github.com/brandonbloom/qplan"&gt;qplan&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
My intention is to use qplan as part of a larger approach to building graphical user interfaces. I expect a that a complete system requires a variety of algorithms and solvers tuned for particular use cases. For example, Adobe's &lt;a href="http://stlab.adobe.com/group__asl__overview.html"&gt;Adam and Eve&lt;/a&gt;, has both a Quickplan solver for property models and a linear constraint solver for layout. An even more complete system may have an FRP evaluation strategy for animations.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/57qQSUireeE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/1693448114096082091/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=1693448114096082091" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1693448114096082091?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1693448114096082091?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/57qQSUireeE/dataflow-solver-qplan-for-clojure.html" title="Dataflow Solver (qplan for Clojure)" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/12/dataflow-solver-qplan-for-clojure.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUMCRngzcCp7ImA9WhNWGEw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-6789087525284077292</id><published>2012-11-23T04:34:00.001-08:00</published><updated>2012-12-17T23:57:47.688-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-12-17T23:57:47.688-08:00</app:edited><title>Templating: Clojure's Backtick</title><content type="html">&lt;div&gt;
&lt;b&gt;[This post describes the motivation for my new Clojure library:&amp;nbsp;&lt;a href="https://github.com/brandonbloom/backtick"&gt;Backtick&lt;/a&gt;]&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
There is no shortage of template languages. In fact, there are probably far too many of them.&amp;nbsp;Template languages fall into two major categories that I'll call&amp;nbsp;&lt;i&gt;procedural&lt;/i&gt;&amp;nbsp;and &lt;i&gt;structural&lt;/i&gt;. Procedural templating languages operate by emitting code as a side-effect. Structural templating languages operate on trees. For example, Ruby's &lt;a href="http://ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html"&gt;ERB&lt;/a&gt; is procedural, where as &lt;a href="http://haml.info/"&gt;HAML&lt;/a&gt; is structural.&lt;/div&gt;
&lt;br /&gt;
The primary advantage of a text-based procedural template language is reach. ERB can be used to generate any type of text file, while HAML can only really be used to emit HTML. Reach is not without disadvantages, however. For one, it's quite easy to write incorrect ERB templates which emit invalid syntax, such as missing a close tag. Additionally, code with side effects can be difficult to refactor because execution order is critical. You can trivially generate SQL statements with an ERB template, but you'll quickly accumulate an incomprehensible mud ball, so you're better off working with a system that treats queries as data and lets you manipulate them structurally. Unfortunately, you'll need to implement a unique structural tool for each target data structure. If you're generating a lot of HTML, then using HAML is a great choice. But if you're only generating one HTML file among a sea of dozens of other arbitrary config file formats, then you really want ERB.&lt;br /&gt;
&lt;br /&gt;
Generalizing, structural templating systems are really just plain old pure functions. You take some data, let's say describing a blog post, and then you return some other data describing the rendered representation of that same post. This is a powerful realization because it enables you to utilize your language's full set of utility functions for refactoring your templates. For an HTML example, see &lt;a href="http://webnoir.org/tutorials/html"&gt;Noir's partials&lt;/a&gt; and their use of &lt;a href="https://github.com/weavejester/hiccup"&gt;Hiccup&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
When you get into the wonderful world of metaprogramming, templating systems encounter a series of new problems. First, there is multi-level escape character hell. Anyone who has ever tried to write a shell script, which substitutes arguments into an Awk script, which generates a config file, knows what I'm talking about. Second, there is the issue of names. Unlike HTML or many simple config files, code generally have one or more notions of context, such as scoping or namespaces. You need to worry problems like&amp;nbsp;&lt;a href="http://www.bookshelf.jp/texi/onlisp/onlisp_10.html"&gt;variable capture&lt;/a&gt;.&amp;nbsp;Yikes!&lt;br /&gt;
&lt;br /&gt;
Early lisps discovered a mechanism for minimizing the pain of escape character hell: quoting. Quoting is similar to escaping, in much the same way that structural templates relate to procedural ones. Escaping lets one combine any two languages, provided the language on top provides a uniform escaping policy. However, with each new layer of languages, there is a new layer of escaping rules and, if those escape sequences conflict, you'll have to double-, or even triple-escape common metacharacters like&amp;nbsp;apostrophes, backslashes, and dollar signs. There's no protection against incorrectly escaped strings and refactoring is thwarted by the need to increment and decrement escaping levels. In contrast, quoting takes advantage of Lisp's &lt;a href="http://en.wikipedia.org/wiki/Homoiconicity"&gt;homoiconicity&lt;/a&gt; to simplify template indirection into four composable, primitive operators: &lt;i&gt;quote&lt;/i&gt;, &lt;i&gt;syntax-quote&lt;/i&gt;, &lt;i&gt;unquote&lt;/i&gt;, and &lt;i&gt;unquote-splicing&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;[If you're already a Clojure syntax-quote pro, you may want to skip down to the next section]&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Due to their high frequency of use, both Common Lisp and Clojure reserve some of their limited syntax for these primitives. Common Lisp uses the&amp;nbsp;apostrophe ('), backtick (`), comma (,), and&amp;nbsp;comma-at (,@) character sequences as shorthand for quote, syntax-quote, unquote, and unquote-splicing respectively. Because Clojure treats commas as whitespace, it substitutes tilde (~) and tilde-at (~@) sequences for the unquoting operators. I'll use Clojure's notation for my examples.&lt;br /&gt;
&lt;br /&gt;
The quote operator is used to protect symbols from evaluation. If I have a symbol &lt;i&gt;x&lt;/i&gt;&amp;nbsp;with value 5, then the expression &lt;i&gt;(quote x)&amp;nbsp;&lt;/i&gt;or &lt;i&gt;'x&lt;/i&gt;&amp;nbsp;does not evaluate to 5, it evaluates to the symbol &lt;i&gt;x&lt;/i&gt;. Quoting is distributed recursively throughout a form. &lt;i&gt;'(f x)&lt;/i&gt;&amp;nbsp;will protect both &lt;i&gt;f&lt;/i&gt;&amp;nbsp;and &lt;i&gt;x&lt;/i&gt;&amp;nbsp;from evaluation. It's this distributive property that allows quoting and unquoting to compose across multiple levels of templating.&lt;br /&gt;
&lt;br /&gt;
You might guess that &lt;i&gt;(unquote 'x)&lt;/i&gt; or &lt;i&gt;~'x&lt;/i&gt; would return 5, but that operation is performed by the &lt;i&gt;eval&lt;/i&gt;&amp;nbsp;function: &lt;i&gt;(eval 'x)&lt;/i&gt; does return 5. Trying to unquote here will generate an error. This distinction is because &lt;i&gt;eval&lt;/i&gt;&amp;nbsp;is generally implemented as a function, not a macro or special form. It does not alter the interpretation of its inputs, so it's behavior doesn't play nice with the distributive property of quoting.&lt;br /&gt;
&lt;br /&gt;
Unquoting comes into play once you introduce syntax-quoting. Syntax-quotes are demarcated by a backtick, which can be thought of as switching Lisp into template mode. Like regular quotes, syntax-quotes are distributive. Unlike regular quote, which leaves its input form alone, syntax-quote transforms its input in two important ways: First it enables unquoting. You can think of unquoting like template variable substitution. Second, it resolves symbols. We'll come back to this second point later.&lt;br /&gt;
&lt;br /&gt;
Let's say that you want to generate the code&amp;nbsp;&lt;i&gt;(f 1 (g 2 3))&lt;/i&gt;&amp;nbsp;where the numbers are provided by variables &lt;i&gt;x&lt;/i&gt; and &lt;i&gt;y&lt;/i&gt; holding the number &lt;i&gt;1&lt;/i&gt; and the vector &lt;i&gt;[2 3]&lt;/i&gt;&amp;nbsp;respectively. You can accomplish this with the expression &lt;i&gt;(syntax-quote (f (unquote x) (g (unquote-splicing y))))&lt;/i&gt;. Well, sort of. Clojure does not provide direct access to &lt;i&gt;syntax-quote&lt;/i&gt; as a symbol.&amp;nbsp;You need to use the shorthand form: &lt;i&gt;`(f ~x (g ~@y))&lt;/i&gt;. Luckily, that's nicer to look at anyway.&lt;br /&gt;
&lt;br /&gt;
Unquote substitutes in a single value, like a dollar-sign in a shell script. Unquote-splicing is a little bit more interesting. When a syntax quote is transforming its inputs, it looks for unquotes and evaluates them. The evaluated result is substituted into the parent expression. Regular unquotes are substituted directly, but splicing unquotes cause the parent expression to be rewritten as a&amp;nbsp;concatenation. The expression&amp;nbsp;&lt;i&gt;`(a b ~@x c d)&lt;/i&gt; is transformed into an expression resembling&amp;nbsp;&lt;i&gt;(concat '(a b) x '(c d))&lt;/i&gt;. The splicing operation is a more powerful alternative to a traditional templating language's looping constructs. It can also emulate a traditional templating language's &lt;i&gt;if&lt;/i&gt; statement. The expression &lt;i&gt;`(f ~@(when b [x]))&lt;/i&gt;&amp;nbsp;will return code that passes the &lt;i&gt;x&lt;/i&gt;&amp;nbsp;argument to &lt;i&gt;f&lt;/i&gt;&amp;nbsp;only when &lt;i&gt;b&lt;/i&gt;&amp;nbsp;is true.&lt;br /&gt;
&lt;br /&gt;
As discussed previously, quoting and unquoting are distributive. This is what allows you to escape from escape character hell. The expression &lt;i&gt;`(f (g x) ~(h y `z))&lt;/i&gt; distributes the quoting behavior down to &lt;i&gt;f&lt;/i&gt;, &lt;i&gt;g&lt;/i&gt;, and &lt;i&gt;x&lt;/i&gt;, but the unquoting operator decrements the quoting level, such that neither &lt;i&gt;h&lt;/i&gt; nor &lt;i&gt;y&lt;/i&gt; are quoted. The &lt;i&gt;z&lt;/i&gt;&amp;nbsp;is explicitly quoted, re-incrementing the quoting level.&amp;nbsp;You can't do that with escape sequences, because, like a procedural template language, there is no explicit notion of the inherent tree structure. It's worth noting that most lisps don't have an explicit quoting level, instead syntax-quote processes its inputs recursively; the quote level is maintained implicitly by the execution stack.&lt;br /&gt;
&lt;br /&gt;
That's not all you need to know about syntax-quote; there's also symbol resolution. Now that you've escaped escape hell and you're writing structural templates, you're able to do bigger and better metaprogramming. If you're writing Common Lisp, you'll quickly run into the aforementioned variable capture problem: You'll generate code that includes name collisions. Clojure's syntax-quote differs from Common Lisp's by providing two mechanisms to combat this problem. First is namespace resolution. Clojure will expand &lt;i&gt;`inc&lt;/i&gt; into &lt;i&gt;clojure.core/inc&lt;/i&gt; and &lt;i&gt;`foobar&lt;/i&gt; into &lt;i&gt;user/foobar&lt;/i&gt;. This protects you from &lt;a href="http://www.bookshelf.jp/texi/onlisp/onlisp_10.html#SEC69"&gt;free symbol capture&lt;/a&gt;. Secondly, Clojure provides automatic gensyms. Symbols ending with a &lt;i&gt;#&lt;/i&gt; character inside a syntax-quote are replaced by an automatically generated name. So the expression &lt;i&gt;`(x# x# y#)&lt;/i&gt; will expand into something like&amp;nbsp;&lt;i&gt;(x__1__auto__ x__1__auto__ y__2__auto__)&lt;/i&gt;, which is virtually impossible to cause &lt;a href="http://www.bookshelf.jp/texi/onlisp/onlisp_10.html#SEC68"&gt;macro argument capture&lt;/a&gt;. As linked several times, &lt;a href="http://www.paulgraham.com/onlisp.html"&gt;Paul Graham's On Lisp&lt;/a&gt; is the best source for understanding these issues more deeply.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;[Experienced Clojurians&amp;nbsp;can start reading from here]&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
OK, so now that you have an extremely powerful template language baked into Clojure's syntax-quoting mechanism, you still have the problem of reach: What good is a template language if you can only generate Clojure code with it? What if you need to generate some HTML? Or what about that Awk script? Unfortunately, the template language can only really yield Clojure forms. Luckily, Clojure forms are relatively rich with a variety of primitives and collection types, so much of the Clojure community uses them for configuration files and DSLs. If you're generating input to these Clojure systems, you're in good shape. You don't have quite the same reach as "all tools that operate on a stream of characters", but "any library in the Clojure ecosystem" is a pretty damn good start. If you're going to create a system to structurally generate SQL queries, you'll need to build some kind of query representation that can be compiled to a SQL string. In a non-homoiconic language, you'd eventually need to come up with a new structural template engine too. In Clojure, your query library will get that template engine for free!&lt;br /&gt;
&lt;br /&gt;
However, there is still a problem. When you first encountered code duplication, you reached for a simple&amp;nbsp;procedural&amp;nbsp;text template. Now you have the escape sequence problem. Lisp mitigates the escape sequence problem with quoting. But once you have larger scale templates, you start running into the variable capture problem. So Clojure solves the variable capture problem with an enhanced syntax-quote. Well just as each prior solution yields a new problem, so too does Clojure's enhanced syntax-quote.&lt;br /&gt;
&lt;br /&gt;
The problem is that Clojure tightly couples the syntax-quote symbol resolution to Clojure's namespace system. This isn't usually a problem if you're always using syntax-quotes to generate code that will ultimately be executed in your local Clojure environment. However, consider the case of generating code to be executed in a *remote* Clojure environment. Or what about generating Clojure forms that will be compiled to SQL procedures to be executed on your database server? In those two cases, it's less likely that resolving symbols against the locally loaded namespaces is a useful behavior. Suddenly, Clojure's powerful template language is unavailable to you! Either you have to piece together your own template system, just as a non-homoiconic language, or you need to play tricks by manually or procedurally defining vars. Ouch.&lt;br /&gt;
&lt;br /&gt;
Enter&amp;nbsp;&lt;a href="https://github.com/brandonbloom/backtick"&gt;Backtick&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Backtick is an absurdly simple library. It provides Clojure's syntax-quote reader macro as a normal macro. You can create new template quoting macros by providing a custom symbol resolver. Check it out!&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/R5qon8LiPHg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/6789087525284077292/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=6789087525284077292" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/6789087525284077292?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/6789087525284077292?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/R5qon8LiPHg/templating-clojures-backtick.html" title="Templating: Clojure's Backtick" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>3</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/11/templating-clojures-backtick.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUIFQH45eSp7ImA9WhNQFkw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-334336270630525010</id><published>2012-11-22T03:51:00.002-08:00</published><updated>2012-11-22T11:45:11.021-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-22T11:45:11.021-08:00</app:edited><title>ClojureScript Projects</title><content type="html">I stumbled across &lt;a href="http://clojure.org/"&gt;Clojure&lt;/a&gt; a few times, but I've fallen in love with it sometime over the past year. I've always loved the &lt;i&gt;idea&lt;/i&gt;&amp;nbsp;of Lisp, but I never loved any particular Lisp until Clojure.&lt;br /&gt;
&lt;br /&gt;
The name "CLoJure" comes from C#, Lisp, and Java. The best parts of each of community clearly shine through. Bolstered by the Java community, Clojure combines all the expressiveness of a Lisp in a package that makes everything seem as easy as C#. That last point deserves elaboration: C# has managed to empower the average code monkey with lambdas, closures, generics, expression trees, and futures. Similarly, Clojure empowers the average senior developer with&amp;nbsp;immutability, macros, flexible dispatch, software transactional memory, interactive development, and a whole lot more! Plus, it's free and open source! Rich Hickey has an incredible talent for distilling good ideas down into understandable bite sized chunks, making agreeable tradeoffs, and then deftly guiding the community.&lt;br /&gt;
&lt;br /&gt;
Lately, I've been spending a lot of my spare cycles working on &lt;a href="http://github.com/clojure/clojurescript"&gt;ClojureScript&lt;/a&gt;. Hacking on the ClojureScript compiler and runtime has been more fun than I can ever recall in my programming career. It's an incredible toolkit that I intend to utilize heavily in my next startup, so I feel like my hours and hours of fiddling with cljs.core is extremely worthwhile. I've already contributed 20+ patches!&lt;br /&gt;
&lt;br /&gt;
As fun as hacking on ClojureScript is, it's probably a better idea to hack on something that &lt;i&gt;uses&lt;/i&gt;&amp;nbsp;ClojureScript, rather than ClojureScript itself. Paul Graham described Hacker News as&amp;nbsp;&lt;a href="http://www.paulgraham.com/hackernews.html"&gt;an application to sharpen Arc on&lt;/a&gt;. Paul is right in saying that you need the top down pressure of a real application to motivate the design and implementation of the language from the bottom up. Although where his goal is &lt;a href="http://paulgraham.com/arcchallenge.html"&gt;brevity&lt;/a&gt;, Rich's goal is &lt;a href="http://www.infoq.com/presentations/Simple-Made-Easy"&gt;simplicity&lt;/a&gt;. While Paul has seemingly effortlessly scaled Hacker News to a 1M+ daily pageview community, Rich has seemingly effortlessly &lt;a href="http://www.datomic.com/"&gt;reinvented the database&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
ClojureScript hasn't found its killer application yet. There is a lot of interest around the project, but only a handful of active contributors and production users. People like &lt;a href="http://www.chris-granger.com/"&gt;Chris Granger&lt;/a&gt;&amp;nbsp;are pushing down on the top with ambitious applications like&amp;nbsp;&lt;a href="http://www.lighttable.com/"&gt;Light Table&lt;/a&gt;, but we need others. Chris is starting to make re-inventing the IDE look effortless, but his application prefers to be installed outside the browser, generally on Macs, so it isn't clear to me if his vision remains squarely in the HTML/CSS/JavaScript-targeting, client/server sweet spot.&lt;br /&gt;
&lt;br /&gt;
It has been over seven years since Gmail and Google Maps has made us re-think what is possible in the browser. I sense that ClojureScript holds the key to the next generation of re-thinking what's possible in client-side applications. As I ponder my next startup, I'm going to be on the lookout for just such an application. In the meantime, I'll be open sourcing some of the technology I'm building as I explore that path.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/8AjAiyyaU9Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/334336270630525010/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=334336270630525010" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/334336270630525010?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/334336270630525010?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/8AjAiyyaU9Q/clojurescript-projects.html" title="ClojureScript Projects" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/11/clojurescript-projects.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEANR3wyeSp7ImA9WhNSGEQ.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-1936676176146542819</id><published>2012-11-02T15:53:00.001-07:00</published><updated>2012-11-02T15:53:16.291-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-11-02T15:53:16.291-07:00</app:edited><title>Personal Update</title><content type="html">&lt;a href="http://techcrunch.com/2012/06/25/salesforce-acquires-techstars-and-techcrunch-disrupt-alum-thinkfuse/"&gt;Thinkfuse was acquired by Salesforce in June&lt;/a&gt;. I spent a few months working at Salesforce's office here in Seattle, but I badly felt the need to get back into the startup world. I'm not really sure what I'm going to do with myself quite yet, but feel free to &lt;a href="mailto:blog@brandonbloom.name"&gt;send me an email&lt;/a&gt;, if you want to grab a coffee.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/uqz3_Scv6NI" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/1936676176146542819/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=1936676176146542819" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1936676176146542819?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1936676176146542819?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/uqz3_Scv6NI/personal-update.html" title="Personal Update" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/11/personal-update.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8HQXg6eCp7ImA9WhVXFkw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-8791396952737539021</id><published>2012-04-16T14:13:00.002-07:00</published><updated>2012-04-16T14:13:50.610-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-04-16T14:13:50.610-07:00</app:edited><title>Learn to Read the Source, Luke</title><content type="html">Apparently, I'm &lt;a href="http://www.codinghorror.com/blog/"&gt;Jeff Atwood&lt;/a&gt;'s guest blogger today: &lt;a href="http://www.codinghorror.com/blog/2012/04/learn-to-read-the-source-luke.html"&gt;Learn to Read the Source, Luke&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
Source code access is near and dear to my heart, so head on over there and read it!&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/5NfbMs06Cxg" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/8791396952737539021/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=8791396952737539021" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/8791396952737539021?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/8791396952737539021?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/5NfbMs06Cxg/learn-to-read-source-luke.html" title="Learn to Read the Source, Luke" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/04/learn-to-read-source-luke.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CUcFQH0zeyp7ImA9WhVTE0w.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7773078865541083049</id><published>2012-02-26T19:23:00.001-08:00</published><updated>2012-02-26T19:23:31.383-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-02-26T19:23:31.383-08:00</app:edited><title>REST: One Thousand Inconsequential Decisions</title><content type="html">&lt;br /&gt;
&lt;div&gt;
Hardly a week goes by without yet another Hacker News front page post by a blogger making a bold declaration about some important thing you &lt;i&gt;absolutely must do&lt;/i&gt;&amp;nbsp;if you want your API to be&amp;nbsp;"RESTful". With &lt;a href="http://weblog.rubyonrails.org/2012/2/26/edge-rails-patch-is-the-new-primary-http-method-for-updates"&gt;Rails' recent PATCH announcement&lt;/a&gt;, REST is all over the front page again.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In my experience designing and consuming APIs of varying levels of RESTfulness, I've concluded that most of the decisions involved in designing a RESTful API are completely inconsequential. PUT vs POST vs PATCH? HATEOAS vs manual URL construction? JSON vs custom Mimetype? It's all the same to an API consuming &lt;i&gt;developer&lt;/i&gt;. It might matter to some kind of generic crawler, but I'm not building an API for crawlers, so I don't care.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
What follows is a series of rants about various parts of the grand REST debate. This is by no means comprehensive. My goal with this post isn't to discourage you from writing a REST API. I simply want you to spend your time on the API decisions that matter, not the cosmetic, inconsequential ones.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;span style="font-size: large;"&gt;HTTP Verbs &amp;amp;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;&amp;nbsp;Status Codes&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The Rails PATCH announcement states "both PUT and PATCH are routed to update". So what's the difference? Apparently PATCH is for partial updates, where as PUT is for complete replacement of resources. Here's the funny bit: Both of these are implemented via POST with a &lt;i&gt;_method&lt;/i&gt; parameter for support of older browsers. The only two HTTP Verbs that actually matter are GET, for idempotent, cachable requests; and POST, for side-effects.&amp;nbsp;Even DELETE is insufficiently defined for any real application. What if you want to support both Archive and Delete like Gmail?&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Which response codes should you use? Those which have specific meaning to HTTP clients. Specifically: those treated differently by browsers. Should you use 201 on create instead of 200? Who cares? Should you use 301 or 302 for redirects? In an API, it doesn't matter. Pick one. Document it. For your actual web page, consult an SEO guide.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
What about errors? If you don't have permission to see a resource, is that a 403? Or do you hide the existence of the resource and return a 404 (like GitHub)? It doesn't matter. Return a 4xx for expected caller errors and a 5xx for unexpected errors. Pick one. Document it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The smart thing to do is to always return your resources within one level of nesting. That is an &lt;i&gt;item&lt;/i&gt;&amp;nbsp;key in JSON or an &lt;i&gt;item&amp;nbsp;&lt;/i&gt;element in XML. Do this so that you can add an &lt;i&gt;error&lt;/i&gt;&amp;nbsp;key or other metadata without breaking changes. For example, you may want a &lt;i&gt;timings&lt;/i&gt; key for perf debugging. For lists, use an &lt;i&gt;items&lt;/i&gt;&amp;nbsp;key because pretty soon, you're going to want a&amp;nbsp;&lt;i&gt;paging&lt;/i&gt;&amp;nbsp;key. Also, for errors, make sure you include a non-localized (and hence, Google-able) error string; a status code is insufficient.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;&lt;span style="font-size: large;"&gt;Hating On HATEOAS&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
HATEOAS, an absurd mouthful of an acronym, is a complete waste of time outside of read-only semi-structured datasets. If you're FreeBase, then go to town with HATEOAS conventions that are useful to you. However, for your typical webapp API, you simply cannot build a useful API client application without deep knowledge of the problem domain and the available API calls. You're going to have API documentation and you're going to have to read it.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
By all means, feel free to use HTTP 201 and a &lt;i&gt;Location&lt;/i&gt; header to return the location of a newly created resource. Or you could just return the resource as JSON with an &lt;i&gt;href&lt;/i&gt;&amp;nbsp;or &lt;i&gt;id&lt;/i&gt;&amp;nbsp;key. That's yet another inconsequential decision. Might as well go with the path of least resistance: return interesting data in the body of the response (ie. in the JSON) where it can be accessed without additional effort to parse the headers. As long as your response code is in the 200 range, no one is going to look at it further.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
I've seen people advocate &lt;i&gt;Link&lt;/i&gt;&amp;nbsp;headers such as &lt;i&gt;rel="newcomment".&lt;/i&gt;&amp;nbsp;It's weird to me because&amp;nbsp;you still need to know which &lt;i&gt;rel&lt;/i&gt; to lookup. The same thing applies to returning a bunch of extra URLs as metadata in your JSON. You still need to know which key to look up for the post-new-comment URL. You might as well just appended &lt;i&gt;"/comments"&lt;/i&gt; and avoid the indirection. &lt;i&gt;Link&lt;/i&gt; and &lt;i&gt;rel&lt;/i&gt; are useful to generic tools that want to treat a wide range of resources the same. There aren't enough common aspects between the domain models of a typical API to justify the spare brain cycles. Again, if you're Wikipedia, your use case is different. You have generic data that you want to traverse generically. And again, that's a mostly read-only use case.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;Versioning&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Some REST fans advocate the use of custom MIME types for API versioning. These folks believe that version numbers in URLs are ugly. The usual objection is that resources should be&amp;nbsp;equitable&amp;nbsp;by URL and that &lt;i&gt;/v1/users/5&lt;/i&gt;&amp;nbsp;and &lt;i&gt;/v2/users/5&lt;/i&gt;&amp;nbsp;are different resources. The problem here is that this resource is actually identified by just the number. You don't need the whole URL and practical constraints are going to make it impossible to equate resources by URL.&amp;nbsp;Consider the constraint of browsers' Same Origin Access Policy. You need&amp;nbsp;&lt;i&gt;api.example.com&lt;/i&gt;&amp;nbsp;to run your public API on it's own IP address. And you need&amp;nbsp;&lt;i&gt;example.com/api/&lt;/i&gt;&amp;nbsp;to make calls from your web frontend without jumping through crazy IFrame interop hacks.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There are two types of API changes: 1) Backward Compatible, and 2) Breaking Changes. In the case of backwards compatible changes, you don't have to, nor should you, change version numbers. Simply add the additional method or additional return data. Nothing to worry about. In the case of breaking changes, you &lt;i&gt;want&lt;/i&gt;&amp;nbsp;to break clients as loudly as possible. If you simply change the MIME type, most consumers of the API are totally going to ignore it. If you deploy your new API at &lt;i&gt;api2.example.com&lt;/i&gt;, then there is no risk of breaking older clients and they have to make a&amp;nbsp;conscious&amp;nbsp;decision to upgrade.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In general, you're probably going to want to protect yourself with API keys. API keys provide the best way to version your API: record the desired API version with the generated key. Just stick an &lt;i&gt;api_version&lt;/i&gt; column on your &lt;i&gt;api_keys&lt;/i&gt; table. Expose that column as a drop down box in your API key management UI. This solution let's you leave the "ugly" version number out of the URL, but still enforce breakage on breaking changes. The best part is that you can totally ignore API keys and versioning when you start: Simply default unregistered API consumers to use v1 when you do implement v2.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
Last word on custom MIME types: One of the great promises of REST is the ability to treat generic things, generically. If your MIME type isn't a standard JSON MIME type, then how can you expect your browser to render it nicely with syntax highlighting and code folding? Your data is structured as JSON or XML; if you want to version the schema, use the facilities provided by XSDs or JSON Schemas, etc. But you're better off with documentation written by humans, for humans, than you are with formal schema.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;span style="font-size: large;"&gt;&lt;b&gt;REST: The Good Parts&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
There are a lot of good reasons why REST has gained popularity. For one thing, it isn't SOAP.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The good ideas in REST are primarily predictable URLs, contractual idempotence, the &lt;a href="http://news.ycombinator.com/item?id=2858712"&gt;preference of nouns over verbs&lt;/a&gt;, and CURL as a client library.&amp;nbsp;In practice, you can get contractual idempotence via GET vs POST. Predictable URLs are no different than predictable function names in all typical library code.&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
All this other stuff, the finer points of URIs and HTTP headers, are completely inconsequential to a developer programming against a well documented API.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/7Vew8CMIVAM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7773078865541083049/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7773078865541083049" title="3 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7773078865541083049?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7773078865541083049?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/7Vew8CMIVAM/rest-one-thousand-inconsequential.html" title="REST: One Thousand Inconsequential Decisions" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>3</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/02/rest-one-thousand-inconsequential.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkAGQXYzfip7ImA9WhRVFUQ.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-1918498034653403238</id><published>2012-01-14T18:22:00.000-08:00</published><updated>2012-01-14T18:32:00.886-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2012-01-14T18:32:00.886-08:00</app:edited><title>Plugging a hole in Microsoft's hiring pipe: IE Frame</title><content type="html">&lt;div&gt;
You're probably familiar with &lt;a href="http://code.google.com/chrome/chromeframe/"&gt;Google Chrome Frame&lt;/a&gt;, Google's clever workaround to combat Internet Explorer dependency in enterprises. You're probably also aware that &lt;a href="http://arstechnica.com/microsoft/news/2009/09/microsoft-google-chrome-frame-makes-ie-less-secure.ars"&gt;Microsoft is not a fan&lt;/a&gt;.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Microsoft desperately needs to implement an equivalent "Internet Explorer 10 Frame" mass deployed as a high-priority Windows Update. Failure to do so all but guarantees that Microsoft will become completely incapable of hiring a sufficient supply of talented graduates.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
The key issue is that two of the major vectors for aspiring engineers are no longer dominated by Microsoft technology.&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Javascript is the New Basic&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
Microsoft QBasic was my first experience with programming, but I got started a few years earlier than most of my peers. The students I graduated college with largely had their first foray into text editing via HTML. Browsers are installed on every computer on the market and web development skills are vital for nearly all professional developers (even those not doing full-time web development). This puts Microsoft in a really bad spot: every budding developer's first impression of Microsoft is "IE SUCKS!!!!11!!one!".&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Microsoft's Stranglehold on Gaming is Loosening&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
When I was employed by Microsoft, I took an informal survey of my recently hired peers. I worked in the gaming division, so my view was skewed: they almost all got into programming via PC gaming. If you loved games, you ran Windows. If you wanted to make games, you learned Microsoft Visual C++. However, times are changing. Apple's iPhone is the biggest gaming platform on the planet. Your family computer is probably a Mac by now and your Xbox doesn't come with a compiler. Why not learn Objective-C? Furthermore, canvas games are popping up everywhere and it won't be long before WebGL is common place. Soon enough, a significant subset of aspiring game developers only have to boot Windows to test something in Internet Explorer. &lt;i&gt;sigh&lt;/i&gt;.&lt;/div&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/nIK4xH98lX8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/1918498034653403238/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=1918498034653403238" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1918498034653403238?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1918498034653403238?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/nIK4xH98lX8/plugging-hole-in-microsofts-hiring-pipe.html" title="Plugging a hole in Microsoft's hiring pipe: IE Frame" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>1</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2012/01/plugging-hole-in-microsofts-hiring-pipe.html</feedburner:origLink></entry><entry gd:etag="W/&quot;C0MEQH4-eyp7ImA9WhdVGUw.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7936160275747534343</id><published>2011-09-24T17:40:00.000-07:00</published><updated>2011-09-24T17:43:21.053-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-09-24T17:43:21.053-07:00</app:edited><title>One year after accidentally falling in love with Rails</title><content type="html">About a year ago, I wrote a post titled&amp;nbsp;&lt;a href="http://blog.brandonbloom.name/2010/09/how-two-pythonistas-accidentally-fell.html"&gt;How two Pythonistas accidentally fell in love with Rails&lt;/a&gt;. Some people predicted that the honeymoon would be over quickly and that I would soon hate it.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;So, do I still love Rails?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Yes. Absolutely.&lt;br /&gt;
&lt;br /&gt;
The prototype and first beta version of &lt;a href="http://www.thinkfuse.com/"&gt;my startup&lt;/a&gt; was built entirely on Rails. The framework enabled us to build our site significantly faster than we would have been able to without it. For that, I still love Rails. It was critical in helping us get to where we are today.&lt;br /&gt;
&lt;br /&gt;
It's the perfect &lt;i&gt;hammer&lt;/i&gt; for the v1, nebulous direction, rapid development,&amp;nbsp;&lt;i&gt;nail&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;That said, we're not using Rails for any new components.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
After getting a lot of feedback from our first beta, we realized we had the wrong approach to the UI.&amp;nbsp;Now, in our second attempt, we've got a much richer client-side front-end, written in &lt;a href="http://jashkenas.github.com/coffee-script/"&gt;CoffeeScript&lt;/a&gt; and bootstrapped by &lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt;. Ruby/Rails is simply not the right tool for that job. In the new world of the browser app, even the folks of 37signals have been accused of&amp;nbsp;&lt;a href="https://github.com/rails/rails/commit/9f09aeb8273177fc2d09ebdafcc76ee8eb56fe33"&gt;propping&amp;nbsp;up their successor&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Luckily, we had a pretty good handle on the models in the problem domain. So I ran `git rm -r app/views` and effectively turned the Rails app into a Json API backend. The models have evolved, but not by much. In fact, there were a few months where the Ruby code remained practically unchanged while we developed the frontend.&lt;br /&gt;
&lt;br /&gt;
As time goes on, we've been breaking our API layer apart into several services. It makes development, deployment, and versioning easier. Service Oriented Architecture is working great for us... now that we know what services we need. These services are each too simple to justify the overhead of Rails. Some are Node.js, some are Sinatra, some are cron jobs and shell scripts. We're polyglots, so we're careful to choose the right tools for each job.&lt;br /&gt;
&lt;br /&gt;
We'd have gotten it very wrong if we tried to build services from the start. The monolithic Rails app was the correct evolutionary intermediate form.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Given a time machine, would I have done anything differently?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Only one thing:&amp;nbsp;&lt;a href="http://seldo.com/weblog/2011/08/11/orm_is_an_antipattern"&gt;I wouldn't have used ActiveRecord&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
I guess&amp;nbsp;&lt;a href="http://blog.brandonbloom.name/2009/10/orms-and-declarative-schemas.html"&gt;I should have seen that one coming&lt;/a&gt;. ORMs are an insidious, broken abstraction. At first, they save you time and make you feel productive. They work out splendidly for a while, but like a virus, they infect every part of your application. One day, you hit that abstraction brick wall and it's too late to do anything about it without significant effort.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;Are there things I hate about Rails?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Ignoring ActiveRecord, only one thing really stands out: Performance.&lt;br /&gt;
&lt;br /&gt;
I'm not even talking about page load times. I'm talking about development time.&lt;br /&gt;
&lt;br /&gt;
Even with our modestly sized application, the impact on productivity is&amp;nbsp;atrocious. Our test suite runs far too slowly to be waited on for every checkin. Rake's startup time is too great to for its usage to be considered "interactive". Simple shell scripts have replaced practically all of our custom tasks; they are instantaneous.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Isn't this basically the same &lt;a href="http://blog.brandonbloom.name/2009/08/dropping-django.html"&gt;story I told about Django&lt;/a&gt;?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Yeah, I guess it is.&lt;br /&gt;
&lt;br /&gt;
The story basically goes:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;Use a framework&lt;/li&gt;
&lt;li&gt;Be productive&lt;/li&gt;
&lt;li&gt;Encounter shortcomings&lt;/li&gt;
&lt;li&gt;Stretch it to work&lt;/li&gt;
&lt;li&gt;Watch it break&lt;/li&gt;
&lt;li&gt;Replace components&lt;/li&gt;
&lt;li&gt;Loop steps three to six&lt;/li&gt;
&lt;li&gt;Be productive&lt;/li&gt;
&lt;li&gt;"Where's the framework?"&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
I still stand by my original&amp;nbsp;criticisms&amp;nbsp;of Django (although, I'm sure much as changed in the last year). I'm also re-affirmed in my belief that big frameworks are generally bad. It just so happens that Rails is good enough and matches my mindset well enough to justify the framework pains.&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/KoANb8ZhVZU" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7936160275747534343/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7936160275747534343" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7936160275747534343?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7936160275747534343?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/KoANb8ZhVZU/one-year-after-accidentally-falling-in.html" title="One year after accidentally falling in love with Rails" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2011/09/one-year-after-accidentally-falling-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DkUARnw6fCp7ImA9WhdQFUs.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-8568685433749424217</id><published>2011-08-16T23:57:00.000-07:00</published><updated>2011-08-16T23:57:27.214-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-08-16T23:57:27.214-07:00</app:edited><title>Stupid brain trick: Tests and vertical splits</title><content type="html">Recently discovered a stupid brain trick that I thought was worth sharing.&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
When writing code with unit tests, I find it helpful to view the tests and the implementations side by side. I use vertical splits in Vim to do this.&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
The stupid brain trick is this: &lt;b&gt;Put your tests in the left-hand split!&lt;/b&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
Why? I traditionally used the left-hand split for the "main" thing I'm working on and the right-hand split for reference. I've watched many other developers do the same thing. The right split is constantly sub-split horizontally and always changing to various different files.&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
In trying to be better about writing tests &lt;i&gt;first&lt;/i&gt;, I created a test code file before the implementation file, so it just happened to be in my left-hand split. After an hour or so of development, I realized that the "main" file placement of the tests meant that I focused more on them. I viewed the actual implementation file as just some ancillary file I had to tweak to get my tests to pass. It changed the focus of what I was doing from "Write a method that does X" to "Describe X". Describing something involves breaking it up in to smaller and smaller pieces until the implementations are so trivial that they basically write themselves.&lt;/div&gt;
&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/giUucWSgX10" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/8568685433749424217/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=8568685433749424217" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/8568685433749424217?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/8568685433749424217?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/giUucWSgX10/stupid-brain-trick-tests-and-vertical.html" title="Stupid brain trick: Tests and vertical splits" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2011/08/stupid-brain-trick-tests-and-vertical.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck8FRH49eyp7ImA9WhZSE0g.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-6054908612527484999</id><published>2011-03-28T15:08:00.000-07:00</published><updated>2011-03-28T15:20:15.063-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-03-28T15:20:15.063-07:00</app:edited><title>Thinkfuse is hiring!</title><content type="html">If you caught the recent &lt;a href="http://techcrunch.com/2011/03/24/seattle-startup-thinkfuse-scores-500k-by-hanging-around-during-y-combinator-demo-day/"&gt;TechCrunch coverage&lt;/a&gt;, then you know that things are heating at my startup, &lt;a href="http://www.thinkfuse.com"&gt;Thinkfuse&lt;/a&gt; :-)&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We've got &lt;i&gt;lots&lt;/i&gt; to do, so we're looking for a kick-ass engineer who can get up and running quickly. My co-founder, &lt;a href="http://stevekrenzel.com/"&gt;Steve&lt;/a&gt;, and I are full-stack developers who really &lt;i&gt;give a shit&lt;/i&gt;, so we're looking for a Seattle-based developer who can keep up, or better yet, force us to struggle to keep up. Rails experience is a plus. Good sense of humor and beer drinking skills, a bigger plus. We're offering a solid salary and meaningful equity.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If you're interested, reach out to me at &lt;a href="mailto:brandon@thinkfuse.com"&gt;brandon@thinkfuse.com&lt;/a&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/GERxNvBF1xk" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/6054908612527484999/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=6054908612527484999" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/6054908612527484999?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/6054908612527484999?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/GERxNvBF1xk/thinkfuse-is-hiring.html" title="Thinkfuse is hiring!" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2011/03/thinkfuse-is-hiring.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D08GQ38ycCp7ImA9WhZXF0g.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-3826999096261613374</id><published>2010-09-17T11:31:00.000-07:00</published><updated>2011-05-07T01:10:22.198-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-05-07T01:10:22.198-07:00</app:edited><title>Where Rails Rocks (and where Python doesn't)</title><content type="html">&lt;div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; background-color: transparent; font-family: Times; font-size: medium; "&gt;&lt;span id="internal-source-marker_0.20409649168141186" style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Here’s a list of things I like about Ruby on Rails. Almost every single one of these are available in at least one Python framework in some form, but the best thing about Rails is how polished the pieces are and how well they all fit together. The whole is truly greater than the sum of its parts.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Fantastic OOBE&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Rails has a fantastic out-of-box experience. Simply execute &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;rails new&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; and you’re off to the races with a directory full of goodies that every real web app needs: dependency management, build system, multi-environment configuration, development storage, logging, custom error pages, robots.txt file and more. See section 3.2 of the &lt;/span&gt;&lt;a href="http://guides.rubyonrails.org/getting_started.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;getting started guide&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; for a complete list. Hell, they even fill out a .gitignore file for you! A place for everything and everything in its place.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Dependency Management: RubyGems&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;In the Python world, easy_install and its ilk are all kinds of broken. You can’t even uninstall a package! Luckily, there is &lt;/span&gt;&lt;a href="http://pypi.python.org/pypi/pip"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Pip and Virtualenv&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;, but assuming Pip supports all your packages, you’ve still got to make sense of dependency freezing and manually setup a virtual environment. With Rails, that all just works: &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;rails server&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; runs in a virtualized environment and loads gems as defined by your &lt;/span&gt;&lt;a href="http://docs.rubygems.org/read/book/1"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Gemfile&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;; &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;rails console&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; for a repl. No need to muck with sys.path! Any seasoned developed has spent many an hour in dependency hell, but it is still hard to justify the infrastructure investment before writing a line of HTML. Bonus points for the “vendor” directory, in case you need to patch a third party package (or Rails itself) and want to keep it under version control.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Build System: Rake&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;I’ll admit it: I like &lt;/span&gt;&lt;a href="http://www.gnu.org/software/make/manual/make.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Makefiles&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. A lot of people really hate them, but they are just so damn useful. Frequently, you need to transform some set of files into some other set of files, but only when they change. Makefiles help you do that reliably. They accomplish their feat explicitly by &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;not&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; being a programming language, but a dependency and build rule declaration language. I’ve seen a lot of attempts at build systems that aim to be idiomatic with respect to their host languages. Java has Ant and Maven. Python has Scons and others. Scala has Simple Build Tool. None of them are as good as plain old Makefiles. Then, I discovered &lt;/span&gt;&lt;a href="http://rake.rubyforge.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Rake&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. Rakefiles rock. They are 90% Makefiles, and 90% Ruby. Rake expertly uses the features of Ruby to craft an API/DSL that looks a lot like Make declarations without compromising on general purpose programmability.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Markup and Style Sheets: Haml and Sass&lt;/span&gt;&lt;br /&gt;&lt;a href="http://haml-lang.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Haml&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; and &lt;/span&gt;&lt;a href="http://sass-lang.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Sass&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; are so amazing, they ought to be illegal. Just click those links and check out the HTML and CSS comparisons. It is no contest. I never want to write HTML or CSS ever again. As far as I’m concerned, HTML and CSS are object code: to be generated by a compiler. Whenever I have to look at the generated code, it feels like I am reading assembly. Simply add &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;gem ‘haml’&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; to your Gemfile, then start writing .html.haml files instead of .html.erb and .sass files instead of .css. Use &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;html2haml&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; and &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;sass-convert&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; to upgrade your crusty old markup and style sheets instantly.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Object Relational Mapper: ActiveRecord&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;I’ve discussed my &lt;/span&gt;&lt;a href="http://blog.brandonbloom.name/2009/10/orms-and-declarative-schemas.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;preference for database schema reflection&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; at great length, so I won’t reiterate here. I will, however, say that &lt;/span&gt;&lt;a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;ActiveRecord&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; makes simple things simple and complex things possible while embodying successful best practices in its various subsystems, like migrations. It doesn’t try to be everything to everyone, unlike &lt;/span&gt;&lt;a href="http://www.sqlalchemy.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;SqlAlchemy&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. And it seems to be nicely onion layered for pealing back when necessary. For example, &lt;/span&gt;&lt;a href="http://github.com/rails/rails/tree/master/activemodel"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;ActiveModel&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; validations can be used on plain old Ruby objects. Which brings me to...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Form Handling: FormBuilder, params and mass assignment&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;I don’t need to learn and manage three different data marshaling techniques to map a database record to a web form. In fact, I don’t need to manage &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;any&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. Schema reflection spares me in the model layer, &lt;/span&gt;&lt;a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;FormBuilder/FormHelpers&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; in the views, and the hash &amp;amp; array structured params in the controllers layer. Almost every user interaction boils down to interpreting a form’s worth of JSON-like data structures as an API call, implemented via a (&lt;/span&gt;&lt;a href="http://github.com/ryanb/trusted-params"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;secured&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;) &lt;/span&gt;&lt;a href="http://railscasts.com/episodes/26-hackers-love-mass-assignment"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;mass assignment&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; to &lt;/span&gt;&lt;a href="http://railscasts.com/episodes/167-more-on-virtual-attributes"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;virtual attributes&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Templating: Layouts, Partials, and Helpers&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;When I’m getting my Haml on, I frequently want to run some non-trivial code. Feel free to shout about your separation of concerns religion, but I’ve got real things to worry about. Furthermore, we as engineers should be writing our production views/templates, not some hypothetical designer who is technical enough to write loops and branching statements, but not to be trusted with a function definition. Views are composed of markup and presentation declarations in the templates, presentation logic goes in helpers (which conveniently share scope with templates and controllers), and are neatly organized into &lt;/span&gt;&lt;a href="http://api.rubyonrails.org/classes/ActionView/Partials.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;partials&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. Simple, but effective.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;URL Design: Routes&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Rail’s &lt;/span&gt;&lt;a href="http://edgeguides.rubyonrails.org/routing.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;routing&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; system makes clever use of blocks to provide a hierarchical, declarative URL map that just makes sense to read. I used to design URLs in an indented text file and then map it to a list of regexps, but the routes.rb file is close enough to a spec and doesn’t require me to even think about regexes, ‘nuff said.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Authentication: Authlogic&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Authentication is hard. Really, really hard. And boring. Really, really boring. But important. Really, really important. Everyone knows that the sign-up funnel is critical to get right and security is easy to get wrong. Most quality sites have a little bit of domain-specific special sauce in their authentication model. Trivial sign-up and log-in forms just don’t cut it. One size simply does not fit all for authentication front-ends, but the authentication back-ends tend to be pretty repeatable. &lt;/span&gt;&lt;a href="http://github.com/binarylogic/authlogic"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Authlogic&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; turns repeatable into re-usable, without the baggage of views you’re going to need to rewrite anyway. Thinking about authentication as CRUD operations on a user-session model blew my mind slightly. I had a full authentication system tuned to my needs up and running within two hours.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Authorization: CanCan&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;I hate &lt;/span&gt;&lt;a href="http://en.wikipedia.org/wiki/Access_control_list"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;ACLs&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. &lt;/span&gt;&lt;a href="http://en.wikipedia.org/wiki/Role-based_access_control"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Role-based authorization&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; simply has too much impedance mismatch for most problem domains. &lt;/span&gt;&lt;a href="http://github.com/ryanb/cancan"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;CanCan&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; is a clever little library which makes zero assumptions about how permissions are represented. Simply declare a list of everything a user can do and how to tell if they may. Query with the &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;can?&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; function, or assert with the &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;authorize!&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; method. Beautiful.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Rope to Hang Yourself: Ruby Itself&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;One keyword in my previous post triggered much discussion: magic. The concept of magic is a topic for a whole ‘nother blog post (as I’ve already been hung a few times), but as several commenters pointed out: it’s all just Ruby. Between mixins, include, require, blocks, method_missing, symbols, and many other features, Ruby is a very capable internal domain specific language development toolkit. Most of the time, you don’t want a DSL. If you’re not an expert in both the host language and the DSL, there is almost certain confusion and maintenance danger. Most projects are either small or distinct enough to justify writing more verbose code that can more readily be understood by successors or even your future self. The gains of a DSL do not always outweigh the costs. Python excels at not supporting DSLs; it is basically executable pseudocode. However, when it comes to a task as common as web application development, it’s worth the time to develop or learn a DSL or two. Rails is loaded with DSL features and “magic” behavior that increases the learning curve, but also increases peak productivity.&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Community&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;These sites have been invaluable: &lt;/span&gt;&lt;a href="http://ruby-toolbox.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;The Ruby Toolbox&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;, &lt;/span&gt;&lt;a href="http://www.railsplugins.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;RailsPlugins&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;, &lt;/span&gt;&lt;a href="http://railscasts.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Railscasts&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. The quantity and quality of &lt;/span&gt;&lt;a href="http://github.com/languages/Ruby"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Ruby libraries on Github&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; continues to impress. The educational infrastructure is extensive. Somehow, the Ruby community seems to consistently be at the forefront of the biggest trends in software development. Much like Rails compared to other frameworks, the community distinctions are subtle, but meaningful. For example, Rubyists championed Git while Python adopted Hg. To the untrained eye, they are virtually identical tools, but extensive experience with both has convinced me that Git is significantly superior. I’m new to the community, but it seems like Rubyists tend to insist on quality and bet on the winning horses.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Other Random Things&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;ul&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Javascript and CSS minification are trivially easy with plug-ins&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Static files are served with version stamps for better caching behavior&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Console and file logging are usefully configured by default&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;a href="http://heroku.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;git push heroku&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;a href="http://rails.vim.tpope.net/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;rails.vim&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;a href="http://www.activemerchant.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;ActiveMerchant&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; looks awesome&lt;/span&gt;&lt;/li&gt;&lt;li style="list-style-type: disc; font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; "&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Only been working with Rails for two weeks; lots more goodness to uncover&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Just who the hell do I think I am?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;I’m nobody. Just some opinionated computer geek working on a start-up. Not unlike many of you! My co-founder and I are currently participating in TechStars Seattle and hope to launch something killer come demo day, November 11th, 2010. We’ve got a grand vision for next generation enterprise collaboration software, but we’re starting small by focusing on making &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;span&gt;&lt;span&gt;&lt;a href="http://www.thinkfuse.com/"&gt;weekly status reports&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;a href="http://www.thinkfuse.com/"&gt;&lt;/a&gt;&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; less painful and more useful. More details soon, but please visit &lt;/span&gt;&lt;strike&gt;&lt;a href="http://www.thisweeklastweek.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;http://www.thisweeklastweek.com/&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; to register for a beta invite&lt;/span&gt;&lt;/strike&gt;&lt;a href="http://www.thinkfuse.com/"&gt;http://www.thinkfuse.com&lt;/a&gt;. Thanks!&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/kPvbfiD8prE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/3826999096261613374/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=3826999096261613374" title="11 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3826999096261613374?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3826999096261613374?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/kPvbfiD8prE/where-rails-rocks-and-where-python.html" title="Where Rails Rocks (and where Python doesn't)" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>11</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2010/09/where-rails-rocks-and-where-python.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CkMBQH48fip7ImA9Wx5XE0o.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-2717690486322200085</id><published>2010-09-13T02:33:00.000-07:00</published><updated>2010-09-13T02:34:11.076-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-09-13T02:34:11.076-07:00</app:edited><title>How two Pythonistas accidentally fell in love with Rails</title><content type="html">&lt;div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; background-color: transparent; font-family: Times; font-size: medium; "&gt;&lt;span id="internal-source-marker_0.5718986766878515" style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;One week ago, &lt;/span&gt;&lt;a href="http://stevekrenzel.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;my co-founder&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; and I found ourselves staring down a thick stack of paper UI prototypes; almost 50 pages total. One week before that, we threw out a perfectly good, working beta product because customer feedback suggested a significantly better path. The tale of how we ended up at that point is entertaining as well, but it will have to wait for another time. This is the tale of how our paper prototype turned a pair of long time &lt;/span&gt;&lt;a href="http://www.python.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Python&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; developers (and &lt;/span&gt;&lt;a href="http://blog.brandonbloom.name/2009/08/dropping-django.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;documented framework haters&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;) into &lt;/span&gt;&lt;a href="http://www.ruby-lang.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Ruby&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; on &lt;/span&gt;&lt;a href="http://rubyonrails.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Rails&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; developers.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Paper prototyping proved to be an extremely worthwhile exercise, but we really wanted something we could click around. We devised a strategy to build a clickable prototype that would evolve into production code. The plan was to stub out all of the views with static HTML and CSS. The CSS could be directly carried over to the product and the HTML could be retrofitted as templates. Having become religious about &lt;/span&gt;&lt;a href="http://sass-lang.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Sass&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; about a year ago, I fired up the file watcher (which automatically rebuilds CSS) and set to work filling a directory with sass and html files.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;After some time, I asked myself “I wonder if &lt;/span&gt;&lt;a href="http://haml-lang.com/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Haml&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; has a &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: 'Courier New'; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;--watch&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; argument?” (it doesn’t) because Sass makes me hate Html’s verbosity. Armed with practically zero Ruby knowledge, I hobbled together a simple &lt;/span&gt;&lt;a href="http://rake.rubyforge.org/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Rakefile&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; and Haml watcher script and got back to work. A few Git pushes later, my co-founder, being the clever bastard that he is, simply installed Rails and the Haml plugin to replace my hacky scripts. “Neat,” I said and thought nothing of it.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Then we designed our URLs in an indented plain text file, which pretty much directly mapped into the &lt;/span&gt;&lt;a href="http://edgeguides.rubyonrails.org/routing.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;routes.rb&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; file and some trivial (empty method) controllers. Whoops! Now we’re writing Ruby.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;“At this point, we might as well try Rails proper.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Generated some models and corresponding migrations: “Wow, explicit schema with reflective model objects. None of this &lt;/span&gt;&lt;a href="http://blog.brandonbloom.name/2009/10/orms-and-declarative-schemas.html"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;declarative schema bullshit&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Wired up some controllers. “The &lt;/span&gt;&lt;a href="http://rails.nuvvo.com/lesson/6371-action-controller-parameters"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;structured params&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; infrastructure is pretty cool.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;“Ruby is kinda fun. &lt;/span&gt;&lt;a href="http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Blocks&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; are super useful.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Integrated &lt;/span&gt;&lt;a href="http://github.com/binarylogic/authlogic"&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 153); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: underline; vertical-align: baseline; white-space: pre-wrap; "&gt;Authlogic&lt;/span&gt;&lt;/a&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;. “Wow, that was easy. I really like how it does not force any front-end decisions on me.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;“Rails is full of evil black magic that I do not understand, but it seems to read my mind, so I don’t really care.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Started to apply some polish and work on the 10% that takes 90% of the time. “I think I can safely add Ruby to my resume now.”&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;Late at night, at the very end of the week, exhausted, we stared at each other. “Dude, I think we can show this off to people tomorrow.” Walking out the door of the office, my co-founder said to me “I feel so dumb for having ignored Rails for so long. I just assumed it was Ruby’s Django”.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;We &lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: italic; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;accidentally&lt;/span&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: bold; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt; implemented our entire beta product in one week, with zero prior Ruby or Rails experience.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: 11pt; font-family: Arial; color: rgb(0, 0, 0); background-color: transparent; font-weight: normal; font-style: normal; text-decoration: none; vertical-align: baseline; white-space: pre-wrap; "&gt;When I have some more time (and experience), I’ll try to write about specific design decisions that set Rails apart.&lt;/span&gt;&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/qiObWJwhCXE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/2717690486322200085/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=2717690486322200085" title="23 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/2717690486322200085?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/2717690486322200085?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/qiObWJwhCXE/how-two-pythonistas-accidentally-fell.html" title="How two Pythonistas accidentally fell in love with Rails" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>23</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2010/09/how-two-pythonistas-accidentally-fell.html</feedburner:origLink></entry><entry gd:etag="W/&quot;D0MMRns_cSp7ImA9WxFWF00.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7667436166817352315</id><published>2010-06-04T19:37:00.001-07:00</published><updated>2010-06-04T19:38:07.549-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-06-04T19:38:07.549-07:00</app:edited><title>NoScript Add-on Install Base</title><content type="html">&lt;span &gt;The &lt;a href="http://noscript.net/" id="zj_s" title="official NoScript site"&gt;official NoScript site&lt;/a&gt; downloads via the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/722/" id="kbbv" title="Mozilla Add-ons page"&gt;Mozilla Add-ons page&lt;/a&gt;, so its download count is probably accurate: 67,616,402&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span &gt;NoScript does not publish their individual add-on usage statistics, but the global download/usage ratio can be calculated from the statistics on the &lt;a href="https://addons.mozilla.org/en-US/firefox/" id="dq5o" title="Firefox Add-on home page"&gt;Firefox Add-on home page&lt;/a&gt;:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;1,962,617,946 add-ons downloaded&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;157,090,095 add-ons in use&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;About 8% of downloaded add-ons are still in use. Assuming NoScript's usage ratio is comparable to the average, approximately 5.4 million installations of FireFox are running NoScript. Let's ignore the fact that NoScript's usage ratio is probably much lower than the average, due to the fact that it &lt;i&gt;breaks most web pages&lt;/i&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;I'd wager that the average NoScript user has at least two machines, so the total number of NoScript users is probably less than 2.7 million.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;&lt;a href="http://www.google.com/publicdata?ds=wb-wdi&amp;amp;met=it_net_user&amp;amp;idim=country:USA&amp;amp;dl=en&amp;amp;hl=en&amp;amp;q=number+of+internet+users+in+america" id="seha" title="There are over 230 million internet users in the USA"&gt;There are over 230 million internet users in the USA&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;Even if 100% of NoScript users were Americans, they form 1% or less of the general population. If you, like me, believe that I have been generous to NoScript here, it is likely that no script users are no more numerous than 1 in 1,000.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;Even if the user is using NoScript, they can whitelist your site. You can probably add &amp;lt;noscript&amp;gt;WARNING: THIS SITE IS BUSTED WITHOUT JS&amp;lt;/noscript&amp;gt; to the top of your page and call it a day. If you are feeling generous, redirect no-script users to the mobile version of your site and tell them why.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span &gt;tldr: Assume that human user agents have Javascript.&lt;/span&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/ZEExgZW1PTA" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7667436166817352315/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7667436166817352315" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7667436166817352315?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7667436166817352315?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/ZEExgZW1PTA/noscript-add-on-install-base.html" title="NoScript Add-on Install Base" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>2</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2010/06/noscript-add-on-install-base.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEUBQXYzcSp7ImA9WxFSEkU.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-3081866171636484955</id><published>2010-04-14T14:55:00.001-07:00</published><updated>2010-04-14T14:57:30.889-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2010-04-14T14:57:30.889-07:00</app:edited><title>Leaving Microsoft</title><content type="html">April 30th will be my last day on the Microsoft XNA team. I'm leaving to start work on my own startup full-time. I also hope to invest some more effort into this blog. Exciting times!&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/Fc0L3CSn62Q" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/3081866171636484955/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=3081866171636484955" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3081866171636484955?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/3081866171636484955?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/Fc0L3CSn62Q/leaving-microsoft.html" title="Leaving Microsoft" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>0</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2010/04/leaving-microsoft.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EHRHw5fSp7ImA9WxNXGEg.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-1471324358664389393</id><published>2009-10-06T10:32:00.001-07:00</published><updated>2009-10-06T13:07:15.225-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-10-06T13:07:15.225-07:00</app:edited><title>ORMs and Declarative Schemas</title><content type="html">My &lt;a id="x_fb" href="http://blog.brandonbloom.name/2009/08/dropping-django.html" title="Dropping Django"&gt;prior post&lt;/a&gt; was more controversial than I anticipated. In hindsight, I should have realized what a hot button issue web frameworks are. One assertion went all but unnoticed. I expect it may be even more controversial: &lt;i&gt;the schema-generative ORM paradigm is fundamentally flawed&lt;/i&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Disclaimer&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I came to this conclusion while working with Django's ORM, but this post is completely object-relational mapper agnostic. We are now using SqlAlchemy, but we are not using any of the many available declarative layers. Instead, we are using schema reflection and semi-automatically configured mappers. This is not an argument against ORMs. It is an argument against generating database schemas from ORM declarations. By extension, this is an argument against Django's ORM because Django uses an exclusively schema declarative model. That said, Django's ORM is far from alone in this camp.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;b&gt;Data Outlives Code&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;When code is dead and gone -- be it through rewrite, obsolescence, or by other means -- the data will still be there. Longevity implies slower evolution; data is always more difficult and riskier to change. Data is also more valuable. What if Facebook rebooted their database? They've already rebooted their software several times.&lt;br /&gt;&lt;br /&gt;Schemas are data. As data, schemas are longer lived, less flexible, and more valuable than code. These factors alone suggest that the database itself should hold the authoritative schema, not a class declaration in the code.&lt;br /&gt;&lt;br /&gt;If you have inherited data from another project, you already know this lesson. You can't generate the schema from code because the schema already exists. You can mimic the authoritative schema in your declarations, but it is easier and more accurate to use reflection.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ineffective Domain Objects&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Object relational mapping is primarily a serialization problem. Every serialization solution has its quirks. The scale and number of quirks seem directly proportional to the absolute difference between the runtime and storage representations. Since a database is a completely distinct type system, rather than an opaque byte array, serialization to a database can have particularly quirky quirks.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;Modeling is one of the fundamental challenges of software development. Capable developers prefer highly expressive or unconstrained type systems to aid with modeling. Generally, runtime type systems are more expressive than those found in databases. Declarative relational mappers, however, constrain the programming language type system to its less expressive counterpart. When building domain objects, the developer must think in terms of the database's type system, not the programming language's.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While using a declarative object relational mapper, developers are effectively trying to design storage and runtime models simultaneously. On average, superior results are achieved by modeling these two concerns separately and then solving an additional subproblem: serialization mapping. You might wind up with slightly more code, but it will be easier to understand and maintain.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;SQL Is Not Going Away&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Despite forcing a less expressive type system on to the developer, declarative ORM layers attempt to treat the database as an implementation detail. However much we wish this were true, the database is not a detail which can be ignored. Sooner or later, you are going to have to open your database shell and write a SQL expression. This requires knowledge of your database's particular SQL dialect and idiosyncrasies. Exacerbating this issue, generated schemas are typically full of name mangling and other ugliness. It is far more pleasant to work with a carefully designed schema than one that compromises for the ORM or the runtime type system.&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;b&gt;Schema and Declarations Diverge&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Data migrations present the most pressing need to work with SQL directly. As an widely unsolved problem, automation can not be trusted. Unless you are painstakingly simulating the schema generator, the production database schema will slowly diverge. Most deviations are tolerable as they will not affect the runtime behavior, but it is wise to minimze differences between production, staging, and development environments. To faciliate this, store migration scripts and backed-up schemas in version control.&lt;br /&gt;&lt;br /&gt;Some deviations will directly affect runtime behavior. For example, consider the case where two versions of an application are running in production. An is_read boolean column was added to a message table in the database. It's default value is false. When a row's page is viewed, the is_read column is set to true. The old version of the application doesn't know about the column, so it can not set the flag. When the new version is rolled out to everyone, affected user will see a bunch of read items marked as unread! The solution is to set the column's default to true, but initialize it to false in the application. Declarations must either deliberately deviate from the schema or present a misleading default value to be overridden during initialization. This is just a simple example, real world schema migrations can be significantly more complex and suffer from numerous more subtle problems.&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/b4Ii_cv65ks" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/1471324358664389393/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=1471324358664389393" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1471324358664389393?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1471324358664389393?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/b4Ii_cv65ks/orms-and-declarative-schemas.html" title="ORMs and Declarative Schemas" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>5</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/10/orms-and-declarative-schemas.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A04BQHg-eyp7ImA9WxNSEUo.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7839271548232117293</id><published>2009-08-24T22:44:00.000-07:00</published><updated>2009-08-24T22:59:11.653-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-24T22:59:11.653-07:00</app:edited><title>URL routing and views</title><content type="html">As promised, I'd like to elaborate on the URL routing system I came up with.&lt;br /&gt;&lt;br /&gt;Weighing at less than 200 lines of code (including example), I'll let it speak for itself: &lt;a href="http://www.brandonbloom.name/static/blog_files/viewdemo.zip"&gt;download it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This approach seems to be working great for us. Love it? Hate it? Feel free to let me know what you think.&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/9sZsmoaEM-s" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7839271548232117293/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7839271548232117293" title="6 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7839271548232117293?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7839271548232117293?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/9sZsmoaEM-s/url-routing-and-views.html" title="URL routing and views" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>6</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/08/url-routing-and-views.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DUIMQHo7fSp7ImA9WxNTF0w.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-7619874311475185718</id><published>2009-08-19T00:07:00.001-07:00</published><updated>2009-08-19T14:33:01.405-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-19T14:33:01.405-07:00</app:edited><title>Dropping Django</title><content type="html">&lt;p&gt;My partner and I built a non-trivial web site on Django. When the next version ships, there might not be a single Django module imported.&lt;/p &gt;&lt;p&gt;We're not trying to drop Django; it is just sort of happening. Piece by piece, it is failing to meet our needs. Despite the marketing copy on the Django site, most components of the framework are tightly coupled enough to make customization frustrating. It is often easier to rewrite core framework components than to implement them on top of the existing  extensibility points.&lt;/p &gt;&lt;p&gt;What follows is a loose chronology of our migration away from Django.&lt;/p &gt;&lt;p&gt;&lt;b&gt;URL Routing&lt;/b&gt;&lt;/p &gt;&lt;p&gt;A flat list of patterns violates the DRY principal when creating nested URLs. Trees are a superior representation. Having a tree of views also enabled us to optionally associate a "binder" function with each node. These bind functions are executed for each URL component from left to right, filling the template context as they go. Breadcrumbs are automatically generated as each node binds, but only the last node executes its full view logic.&lt;/p &gt;&lt;p&gt;&lt;b&gt;Authorization&lt;/b&gt;&lt;/p &gt;&lt;p&gt;Our site enforces permissions on every resource, but Django's database ACLs would have been prohibitively numerous. Instead, views or their URL binders may raise an AccessDenied exception. Upon catching such an exception, a middleware layer serves a login form. This ensures users have permission to access the current resource, as well as all ancestor resources bound to the URL.&lt;/p &gt;&lt;p&gt;&lt;b&gt;Authentication&lt;/p &gt;&lt;p&gt;&lt;/b&gt;Both of Django authentication's key extensibility points are flawed. These two extensibility points are "user profiles" (storing additional per-user data) and custom credentials (such as for logging in via email address instead of username). Django's documentation and numerous internet sources cover both topics, but all of the guidance lacks important caveats. The admin UI, in particular, is very easy to break with either extensibility mechanism.&lt;/p &gt;&lt;p&gt;Extending the User model with the ORM requires a one-to-one database relationship. This relationship can be implemented with a "user profile" setting, an explicit foreign key, or model inheritance. Each approach has its own strengths and weaknesses in terms of performance, API semantics, subtle behavioral changes, and outright bugs.&lt;/p &gt;&lt;p&gt;Enabling custom credentials requires implementing a trivial authorization "backend" object. Unfortunately, it is non-trivial to replace usernames with email addresses. The admin UI's login form refuses to accept email addresses without hacking the template. Even if you hacked the template, the User model would still enforce a non-null constraint on the username field and the generated database schema enforces a uniqueness constraint as well. It turns out to be easier to fill the username field with a dummy value and "support" both forms of authentication with your backend, but you won't come to that conclusion until your head has already bore a hole in your desk.&lt;/p &gt;&lt;p&gt;&lt;b&gt;Templating&lt;/p &gt;&lt;p&gt;&lt;/b&gt;We do our best to keep view and template logic separate. Django's templates are targeted at designers, who aren't implementing any real logic anyway. However, we're a pair of hackers. Sometimes it is just more convenient to put a little bit of logic in the views. Besides, templates are code; code needs to be reviewed and tested. We wouldn't ever hire a designer who couldn't pass a code review for some trivial template logic.&lt;/p &gt;&lt;p&gt;We needed a pragmatic template language to replace Django's idealistic one. Any template language with greater expressive power would have been welcome, but Jinja2 fit the requirements and provided the easiest migration path. Ultimately, we'd prefer to use something like HAML, but there doesn't seem to be a Python equivalent besides the inactive GHRML project. We are, however, using SASS. I will never write CSS by hand again.&lt;/p &gt;&lt;p&gt;&lt;b&gt;ORM and Admin UI&lt;/b&gt;&lt;/p &gt;&lt;p&gt;One of Django's most touted features is the Admin UI. For simple "active record" style database models, the Admin UI is a huge time saver. Sadly, it struggles a little bit with nullable fields and is tricky to customize. You'll definitely need to write custom UI for complex models, but by and large the admin solves the problem at hand: viewing, creating, updating, and deleting database rows.&lt;/p &gt;&lt;p&gt;After using the Admin for a little, I found myself missing Microsoft Access. I never thought I'd say that, but it is true. &lt;strike&gt;Django's admin does not support sorting, filtering, or other impromptu queries.&lt;/strike&gt; &lt;small&gt;&lt;i&gt;Edit: It turn's out I was mistaken about sorting and filtering, but I stand by the core message of this section.&lt;/i&gt;&lt;/small&gt; I found myself writing impromptu queries in the database and Python shells. After a while, I just gave up and installed a desktop client. I haven't visited the Admin UI since.&lt;/p &gt;&lt;p&gt;Django's ORM has shortcomings with respect to querying, especially for joins and aggregation. It has been improving over time, but it will likely never reach the capability of projects solely focused on databases, such as SqlAlchemy. With the admin having fallen into disuse, the Django ORM lost all advantage. Beyond Django's specific weaknesses, I've come to believe that the schema-generative ORMs paradigm is fundamentally flawed. That is a topic that deserves an entire (Django-agnostic) post of it's own. We are now using SqlAlchemy via schema reflection; no declarative layer.&lt;/p &gt;&lt;p&gt;&lt;b&gt;Form Validation and Generation&lt;/b&gt;&lt;/p &gt;&lt;p&gt;Here is where our chronology meets present day. We are still using Django form validation, but never used form generation beyond scaffolding. Nearly all of our templates customize labels and display of errors. Additionally, embedding widget information in the Python code is cumbersome during template development. Django forms is a quality validation library, but there are some inconsequential style things that I like better about FormEncode. Preferences aside, the difference isn't large enough to justify switching.&lt;/p &gt;&lt;p&gt;While I like FormEncode, I'm still not sold on its anti-form-generation companion, htmlfill. I think there is a middle ground with form generation that provides scaffolding during development, smoothly transitions to production use, and cooperates with validation. As we implement more complex client views, I'll be on the lookout for ways to improve our form development toolbox.&lt;/p &gt;&lt;p&gt;&lt;b&gt;So, ugh... What's left? &lt;/b&gt;&lt;/p&gt;&lt;p&gt;Besides a few isolated helper functions, not much is left of Django.&lt;/p &gt;&lt;p&gt;The last big ticket item is the HTTP framework and WSGI server. We could continue using Django as if it were CherryPy or Paste, but Django has this nasty habbit of insisting on running your code for you. The settings and manage.py infrastructure are fiddly for deployment and don't really add any value over simple scripts using our application like a library. Might as well use a simpler WSGI library, and replace those over-engineered management/commands/foo.py files with vanilla scripts/foo.py files.&lt;/p &gt;&lt;p&gt;&lt;b&gt;Moral of the Story&lt;/b&gt;&lt;/p &gt;&lt;p&gt;I'm sure there are numerous lessons to be generalized from this journey. Personally, I've developed a moderate fear of the word "framework", as well as altered the way I think about software abstractions. I think the most important lesson, however, is one I already knew: choose the right tool for the job. Unfortunately, we had no idea what the right tool was when we started. I'm not sure we know any better now.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/cCerFANbN90" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/7619874311475185718/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=7619874311475185718" title="40 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7619874311475185718?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/7619874311475185718?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/cCerFANbN90/dropping-django.html" title="Dropping Django" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>40</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/08/dropping-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEYBR348fCp7ImA9WxJXGEs.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-4006940402542870204</id><published>2009-06-12T20:02:00.001-07:00</published><updated>2009-06-12T20:02:36.074-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-06-12T20:02:36.074-07:00</app:edited><title>AppWeek</title><content type="html">&lt;p&gt;&lt;a href="http://blogs.msdn.com/shawnhar/archive/2009/06/12/appweek.aspx"&gt;Shawn's AppWeek post&lt;/a&gt; inspired me to write one too. AppWeek is our chance to be creators for a little while and it was a lot of fun. I didn't set out to build something nearly as ambitious as &amp;quot;Super Avatar Sample Smashup EXTREME! - 'Capture the Cat' edition&amp;quot;, but I did get to take a swing at a game I've wanted to build for a while: &lt;del&gt;Rock'em Sock'em Avatars&lt;/del&gt; Avatar Boxing. Avatars, being a new feature in this release, were an unwritten requirement for all of the AppWeek games. Between SASSECTCE, my game, and the many others, Avatars were chasing cats, beating each other up, play futuristic sports, falling off buildings, dancing in a cloud of gems, being launched from canons to save the world, and much more. All this excitement was almost too much for a bunch of exhausted engineers, but that's what the beer was for during the game unveilings.&lt;/p&gt;  &lt;p&gt;Here's what the game looked like with the basic animations wired up. You'll notice that the avatars have been hitting the gym. That's because their arms were too short to reach each other! I added a little extra bulk because I was laughing too hard not to. I directly bound the game pad triggers to the shoulder and elbow joints and rigged up the chase camera sample to inspect my work. There wasn't much game play yet, but it was already fun. That's always a good sign.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JRoal87eB78/SjMWErtvNwI/AAAAAAAAASg/KNCPdBd4tyI/s1600-h/bdbx1-1%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-1" src="http://lh5.ggpht.com/_JRoal87eB78/SjMWGJ31oUI/AAAAAAAAASk/Rc7YMqpoXPk/bdbx1-1_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JRoal87eB78/SjMWTtx6BxI/AAAAAAAAASo/8wyqMFcIBQI/s1600-h/bdbx1-2%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-2" src="http://lh3.ggpht.com/_JRoal87eB78/SjMWW4Vkk8I/AAAAAAAAASs/ZKAP9xib_m8/bdbx1-2_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;Even with just one week, I decided to invest some time into debugging visualizations. That turned out to be a really great idea.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JRoal87eB78/SjMWYjC74LI/AAAAAAAAASw/Wor0Vg8KzqE/s1600-h/bdbx1-5%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-5" src="http://lh5.ggpht.com/_JRoal87eB78/SjMWZGEBb1I/AAAAAAAAAS0/etLyKATKNqA/bdbx1-5_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Then, I added some collision spheres for the heads, hands, and upper bodies. This was a hacky, trial and error process. Thankfully, C# compiles quickly.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_JRoal87eB78/SjMWcFD8z8I/AAAAAAAAAS4/2ceLPhGwDQU/s1600-h/bdbx1-4%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-4" src="http://lh5.ggpht.com/_JRoal87eB78/SjMWdsfPpWI/AAAAAAAAAS8/yxdpSUsG3wA/bdbx1-4_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt;&amp;#160; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JRoal87eB78/SjMWjHxpusI/AAAAAAAAATA/Exib_dAenPs/s1600-h/bdbx1-3%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-3" src="http://lh3.ggpht.com/_JRoal87eB78/SjMWk13YF9I/AAAAAAAAATE/qBXF2hUqIBo/bdbx1-3_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;At this point, I spent an entire day working on the physics. I wanted the avatars to bounce/wobble when they got hit, so I rigged up some complex spring systems. Things were starting to work, but I'm generally pretty bad at this sort of thing and my simulation routinely exploded. The avatars arms went shooting off into space and I was getting pretty frustrated. No screen shots of that chaos because I am embarrassed.&lt;/p&gt;  &lt;p&gt;With half a day to go, I added the obligatory damage bars and some rudimentary hand-to-head collision detection.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_JRoal87eB78/SjMWpC6hp8I/AAAAAAAAATI/w4NN2y3gHww/s1600-h/bdbx1-6%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-6" src="http://lh5.ggpht.com/_JRoal87eB78/SjMWqLAx2mI/AAAAAAAAATM/QRuBL6DiPfo/bdbx1-6_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;I was feeling pretty good about the game, despite my physics failures, it was pretty fun anyway. I wondered down the hall to chat with Jace, who had just added sound effects to his game. His game was hilarious before, but the sound effects were &lt;em&gt;priceless&lt;/em&gt;. I ejected the sound effect CD out of his machine, yoinked it, and took off running. An hour later (and 10 minutes &lt;em&gt;after&lt;/em&gt; the deadline), my game had some sweet punch and miss sounds. I also made the avatars' heads pop up when their damage bar was full, accompanied by an awesome zip-tie sound.&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_JRoal87eB78/SjMWxWjyCCI/AAAAAAAAATQ/RfSo9rXaqqk/s1600-h/bdbx1-7%5B2%5D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; border-top: 0px; border-right: 0px" border="0" alt="bdbx1-7" src="http://lh4.ggpht.com/_JRoal87eB78/SjMWx7_lM2I/AAAAAAAAATU/P7RHzLPCFH0/bdbx1-7_thumb.png?imgmax=800" width="244" height="139" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;At our team happy hour, I'd like to think Avatar Boxing was a fan favorite. I certainly had fun making it! I hope everyone enjoys Avatar support in the new XNA Game Studio.&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/G3M7uTX2ey0" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/4006940402542870204/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=4006940402542870204" title="5 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/4006940402542870204?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/4006940402542870204?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/G3M7uTX2ey0/appweek.html" title="AppWeek" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://lh5.ggpht.com/_JRoal87eB78/SjMWGJ31oUI/AAAAAAAAASk/Rc7YMqpoXPk/s72-c/bdbx1-1_thumb.png?imgmax=800" height="72" width="72" /><thr:total>5</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/06/appweek.html</feedburner:origLink></entry><entry gd:etag="W/&quot;Ck4GQXc7fCp7ImA9WxJTEk8.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-1269333104236652268</id><published>2009-04-20T02:33:00.001-07:00</published><updated>2009-04-20T02:48:40.904-07:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-04-20T02:48:40.904-07:00</app:edited><title>PowerShell: condemned to reinvent</title><content type="html">&lt;p&gt;I tried PowerShell when it was first released, but never used it for real work. I recently attended a &amp;quot;brown bag&amp;quot; presentation about PowerShell. This presentation spurred me to augment our team's environment with PowerShell and I have been using it every day since.&lt;/p&gt;  &lt;p&gt;In the past weeks using and abusing PowerShell, I have drawn two conclusions:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;PowerShell has a killer set of standard tools with brilliantly designed usability.&lt;/li&gt;    &lt;li&gt;The PowerShell team doesn't understand UNIX and therefore were condemned to reinvented it, poorly -- with apologies to Henry Spencer.&lt;/li&gt; &lt;/ol&gt;  &lt;p&gt;First things first: if you spend any time working with Windows, get PowerShell. Now. Stop reading my blog and go &lt;a href="http://www.microsoft.com/windowsserver2003/technologies/management/powershell/download.mspx"&gt;download it&lt;/a&gt; immediately. It mops the floor with cmd.&lt;/p&gt;  &lt;p&gt;The key premise behind PowerShell is that it operates on live .NET objects. This is beneficial because it eliminates a lot of the text cutting and manipulation common in shell scripts. Additionally, it puts the full .NET Base Class Library into your scripting toolbox. PowerShell tools, known as commandlets, typically only render the most common fields for their objects, but the less common fields are easily available in memory. By convention, Commandlets are named with a &lt;em&gt;verb&lt;/em&gt;-&lt;em&gt;noun&lt;/em&gt; pattern and support a common command line parsing behavior. The repository of commandlets and the command line options of each are easily queried and highly consistent. All this meta-data makes PowerShell a breeze to learn.&lt;/p&gt;  &lt;p&gt;I fell in love with the the discoverablity and ease of use when I tried to kill a collection of runaway processes:&lt;/p&gt;  &lt;pre class="code"&gt;PS&amp;gt; get-command -noun process&lt;br /&gt;&lt;br /&gt;CommandType   Name           Definition&lt;br /&gt;-----------   ----           ----------&lt;br /&gt;Cmdlet        Get-Process    Get-Process [[-Name] &amp;lt;String[]&amp;gt;] [-Verbo...&lt;br /&gt;Cmdlet        Stop-Process   Stop-Process [-Id] &amp;lt;Int32[]&amp;gt; [-PassThru]...&lt;br /&gt;&lt;br /&gt;PS&amp;gt; get-process notepad | stop-process&lt;br /&gt;PS&amp;gt; get-alias | where { $_.definition.contains(&amp;quot;Process&amp;quot;) }&lt;br /&gt;&lt;br /&gt;CommandType   Name   Definition&lt;br /&gt;-----------   ----   ----------&lt;br /&gt;Alias         kill   Stop-Process&lt;br /&gt;Alias         ps     Get-Process&lt;br /&gt;&lt;br /&gt;PS&amp;gt; ps someotherapp | kill&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;OK, that's pretty cool and oh-so-very Unixy -- right? Wrong. Notice the &amp;quot;CommandType&amp;quot; column in the results of get-command. There are many other types of commands besides commandlets: functions, filters, scripts, applications, etc. Each of these has slightly different semantics for pipes and parameters. Applications, for example, have no way of accepting .NET object pipes. You must develop a separate commandlet. Yikes!&lt;/p&gt;&lt;p&gt;Compare to Unix: all commands are applications which accept a command line and pipe byte streams in and out. Much simpler, but byte streams aren't as friendly, discoverable, and maintainable as object streams. However, the brilliantly simple thing about Unix is that, when you get right down to it, object streams are just byte streams! There is absolutely nothing stopping you from implementing &lt;em&gt;get-process&lt;/em&gt; and &lt;em&gt;stop-process&lt;/em&gt; as Unix programs which pipe object references, JSON, pickled Python objects, XML, S-expressions, or any other data format you fancy. Doug Mcllroy, the inventor of Unix pipes, was right: text streams are the universal interface.&lt;/p&gt;&lt;p&gt;Actually, this is no different on Windows. All of the PowerShell commandlets could have been implemented as applications which import a library. This library would replace &lt;em&gt;main&lt;/em&gt; in much the same way as &lt;em&gt;winmain&lt;/em&gt;, provide a metadata enriched implementation of &lt;a href="http://www.gnu.org/software/hello/manual/libc/Getopt.html"&gt;&lt;em&gt;getopt&lt;/em&gt;&lt;/a&gt;, &lt;em&gt;man&lt;/em&gt;, etc. There is no need to invent a new shell in order to acquire the power of piping objects. Sure, cmd is old and needed to be retired for many other reasons, but it is a real shame that the PowerShell toolset is not available to those of us stuck in batch scripts.&lt;/p&gt;&lt;p&gt;Personally, I would really like to see such a library developed. Microsoft has certainly proved one thing with PowerShell: steep learning curves are not intrinsic to command line interfaces. Unfortunately, commandlets are two steps forward and one step backwards. I have no doubt that we can retake that forward step.&lt;/p&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.code, .code pre&lt;br /&gt;{&lt;br /&gt;	font-size: small;&lt;br /&gt;	color: black;&lt;br /&gt;	font-family: consolas, "Courier New", courier, monospace;&lt;br /&gt;	background-color: #ffffff;&lt;br /&gt;	/*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;.code pre { margin: 0em; }&lt;br /&gt;&lt;/style&gt;  &lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/wmMwZMyjFQo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/1269333104236652268/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=1269333104236652268" title="7 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1269333104236652268?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/1269333104236652268?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/wmMwZMyjFQo/powershell-condemned-to-reinvent.html" title="PowerShell: condemned to reinvent" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>7</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/04/powershell-condemned-to-reinvent.html</feedburner:origLink></entry><entry gd:etag="W/&quot;DEIBRH04cCp7ImA9WxVXEUo.&quot;"><id>tag:blogger.com,1999:blog-4887447608343482497.post-757509981108407907</id><published>2009-02-09T02:49:00.001-08:00</published><updated>2009-02-09T02:49:15.338-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2009-02-09T02:49:15.338-08:00</app:edited><title>Language-oriented programming: too much, too fast</title><content type="html">&lt;p&gt;There was some &lt;a href="http://news.ycombinator.com/item?id=452919"&gt;recent discussion on Hacker News&lt;/a&gt; about the &lt;a href="http://www.onboard.jetbrains.com/is1/articles/04/10/lop/index.html"&gt;2004 article on &amp;quot;Language Oriented Programming&amp;quot;&lt;/a&gt; by &lt;a href="http://www.sergeydmitriev.com/"&gt;Sergey Dmitriev&lt;/a&gt;. With my growing interest in programming languages, this article, and the pending release of &lt;a href="http://www.jetbrains.com/mps/index.html"&gt;JetBrain's Meta Programming System&lt;/a&gt;, I have been thinking a lot about the future of programming.&lt;/p&gt;  &lt;p&gt;As Sergey points out, MPS is not the first entry into this paradigm of software development. &lt;a href="http://en.wikipedia.org/wiki/Intentional_programming"&gt;Intentional Programming&lt;/a&gt; was being &lt;a href="http://www.youtube.com/watch?v=tSnnfUj1XCQ"&gt;demonstrated by Microsoft Research&lt;/a&gt; as early as 2000. Wikipedia lists several implementations of the &lt;a href="http://en.wikipedia.org/wiki/Language-oriented_programming"&gt;language-oriented programming&lt;/a&gt; concept. (OK, that is enough links for now!) Sadly, none of these systems have been met with wide spread success. Despite my unlimited respect for the JetBrains team and love of their products -- especially Resharper -- I expect MPS to fail to achieve critical mass. I don't think many programmers would disagree. It's just too much, too fast.&lt;/p&gt;  &lt;p&gt;Software development needs to evolve, not start anew. If you change too much at once, the common developer simply won't accept it. Language designers are still re-inventing Lisp piece by piece in an effort to make sense of all the different concepts for &lt;a href="http://www.nikhilk.net/Personas.aspx"&gt;Mort&lt;/a&gt;. In order to improve adoption of language-oriented programming concepts, the critical path must be identified and the programming community must be lead down that path one step at a time.&lt;/p&gt;  &lt;p&gt;I believe that the first stepping stone is the box editor.&lt;/p&gt;  &lt;p&gt;Both MPS and Intentional Programming provide a visual tree of boxes editor and store the abstract syntax tree in a sort of source code database. Both of these concepts are critical to power of language-oriented programming, but changing the storage of source code is simply too radical of a change. Well, actually, source code is routinely stored in databases for IDE features, but the authoritative storage always remains text files. All of a programmers most trusted tools operate at the string level and it is simply impractical to throw everything out at once.&lt;/p&gt;  &lt;p&gt;This is why I propose the creation of a new editor which uses an abstract syntax tree box editing model, but preserves the source as text. This editor would have to play nice with other developers who are using traditional text editors. It should be possible for a single developer to try the editor for a day or two without another collaborator even noticing. There are clear benefits to a box editor even in the absence of language-oriented features. For example, navigation of source code by parent, child, or sibling relationships instead of by word or by line. Advanced renders can be used to layout math expressions or to show a referenced image file in a comment.&lt;/p&gt;  &lt;p&gt;Editors are a religion, start a new one. If some box editor becomes popular enough, other tools would spring up around them. To help the process, the editor should have well exposed extension and composition mechanisms. For example, it should be trivially easy to add new renderers for various nodes. It should be equally trivial to utilize the abstract syntax tree system to create stand alone code analysis tools.&lt;/p&gt;  &lt;p&gt;If box editors garner enough of a following, then the language-oriented programming advocates can seek the next stepping stone.&lt;/p&gt;  &lt;img src="http://feeds.feedburner.com/~r/YouWorkForMeComputer/~4/E8zHi7TkgK4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://blog.brandonbloom.name/feeds/757509981108407907/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.blogger.com/comment.g?blogID=4887447608343482497&amp;postID=757509981108407907" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/757509981108407907?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/4887447608343482497/posts/default/757509981108407907?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/YouWorkForMeComputer/~3/E8zHi7TkgK4/language-oriented-programming-too-much.html" title="Language-oriented programming: too much, too fast" /><author><name>Brandon Bloom</name><uri>https://plus.google.com/114179817210743283438</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="//lh4.googleusercontent.com/-wzrCTMzz79I/AAAAAAAAAAI/AAAAAAAAA8k/2XW1iKyrd-I/s512-c/photo.jpg" /></author><thr:total>4</thr:total><gd:extendedProperty name="commentSource" value="1" /><gd:extendedProperty name="commentModerationMode" value="FILTERED_POSTMOD" /><feedburner:origLink>http://blog.brandonbloom.name/2009/02/language-oriented-programming-too-much.html</feedburner:origLink></entry></feed>
