Single Page Applications (SPA)

Summarized using AI

As Easy As Rails

Justin Searls • February 06, 2014 • Burbank, CA

In his talk titled "As Easy As Rails" at LA RubyConf 2014, Justin Searls discusses the limitations of the Rails framework in developing modern web applications, particularly those requiring fat-client JavaScript user interfaces. He opens with a metaphor about baking, comparing the unexpected evolution of customer requests for a cupcake to developers' tendency to default to Rails for web applications. Searls argues that developers often favor the convenience of Rails and its server-side capabilities, leading to sub-optimal choices for client-side tools, particularly JavaScript.

Key points include:

  • Rails Convenience vs. JavaScript Complexity: Although Rails simplifies server-side development, many Ruby developers find themselves poorly equipped for client-side challenges, leading to an over-reliance on Rails. Searls notes that better JavaScript applications are often developed by those outside the Ruby community.

  • Monolithic Architecture Problems: Rails excels at handling traditional document-driven applications. However, when trying to build fat-client JavaScript applications within the Rails framework, developers often create a convoluted architecture that blurs the lines between frontend and backend responsibilities.

  • The Shift in Web Development: Searls highlights the rapid advancements in the Node.js ecosystem and JavaScript tooling, which he argues have outpaced Ruby's offerings in recent years. Newer technologies are better suited for building modern single-page applications which Rails is not inherently designed for.

  • Tuition of Tooling: He discusses various development tools available in both the Ruby and Node.js ecosystems, emphasizing that while Ruby's tooling is mature, Node.js is rapidly evolving with innovative solutions that often cater to modern web needs more effectively.

  • Lineman Framework: Searls introduces Lineman, a framework he has developed, which facilitates easier development of JavaScript applications by providing tasks automation, configuration, and integration with tooling like Grunt, thus improving productivity significantly.

Searls concludes by stating that while Rails has been foundational for many web developers, recognizing its limitations is crucial for successful modern application development. Emphasizing that splitting monolithic applications into distinct front and back-end services can enhance organization, clarity, and efficiency, he advocates for treating JavaScript as a first-class citizen in application development. Searls invites developers to experiment with modern development setups that leverage the strengths of tools like Lineman to transform their workflow and output more effectively.

As Easy As Rails
Justin Searls • February 06, 2014 • Burbank, CA

Rails came to prominence because it makes web development easy. So easy, in fact, that we're afraid to ask if Rails might be the wrong choice for our web application. Because before Rails, web development was so much more painful and difficult. It turns out that Rails is an awful choice for at least one type of web application: fat-client JavaScript user interfaces. We've been slow to admit this because it's hard to deny the comfort and convenience of the Rails ecosystem. But viewed more broadly, the Ruby ecosystem's client-side tooling has been completely outflanked in the past two years by the tremendous community focus on Node.js and Grunt. In this talk, we'll discuss why building single page apps with Rails isn't as easy as we might assume. We'll uncover the dangers of tangling our front-end UI with our backend-services in a single repository. Finally, I'll demonstrate some of the amazing things that development tools are capable of when JavaScript is treated as a first-class language and when (just like in Rails) we strive to make developers' work easier.

Help us caption & translate this video!

http://amara.org/v/FG2q/

LA RubyConf 2014

00:00:06.000 Hello everyone!
00:00:26.240 I'm super glad to be here. My talk's title is "As Easy As Rails," which is hopefully a bit confusing because I'm going to explain it.
00:00:33.600 My name is Justin Searls. Thank you, Koby. I come from a company called Test Double.
00:00:38.960 We're a consultancy based in Columbus, Ohio, here to make friends. We know a lot of great developers in the Midwest.
00:00:45.840 We've been traveling to conferences in Europe and across the country, and this is my first time ever really in Southern California. I'm really, really glad to be here.
00:00:52.559 I'd love to meet you today! If I don't get the chance, I would appreciate any feedback on Twitter or via email to say hello. We're just looking to connect with people in the area.
00:01:05.439 This talk is about three of my favorite things: first, cupcakes; second, the planet Earth; and third, monolithic application architecture.
00:01:16.479 Imagine you own a bakery. A customer comes to you wanting something sweet. You bake a cupcake for him. But then he says, "On second thought, I wanted it a little sweeter, maybe with a little extra crunch." So you add some sprinkles.
00:01:28.799 Then the customer says, "I'm sorry to do this, but I’d really love it if it were filled with hot fruit filling." And at this point you think, "Oh gosh, the cupcake was the wrong abstraction!"
00:01:36.880 What the customer really wanted was a fresh, hot baked pie. It's an honest mistake that happens, but if you found yourself in a workflow where every time a customer came in you immediately assumed they wanted a cupcake, that would create a problem.
00:01:49.040 Now, suppose you own a software studio. A customer comes in and says they need a web application, so you immediately start a new Rails project. Then they say, "That graph looks nice, but I need zoom, filters, and more dynamic behavior." So you sprinkle some JavaScript on top.
00:02:02.000 Then they say, "I really need this to feel more like a native app. I want all these other advanced interactive features." And then you think, "Ah, what they really need is a fat-client JavaScript application." To the extent that Rails exists, it's just off to the side providing JSON services.
00:02:19.360 In fact, it doesn't necessarily need to be Rails at all. It's an honest mistake. I run into a lot of people at software studios who seem to follow this workflow regularly, and I'm here to address the problem today.
00:02:30.720 I think the reason this keeps happening, especially in the Rails community, is that Rails makes life too easy. When I work in a Rails context, I have all these amazing server-side tools that are so convenient.
00:02:45.440 And when I talk to Rails developers about their favorite client-side tools, they usually come up empty. No one hates JavaScript more than Ruby developers, although that's hyperbole—it's an exaggeration. Client-side tools do exist, but they’re often terrible and rusty.
00:03:08.400 There's a value proposition we make in our minds whenever a new application or feature comes to us, where we decide what the best technological approaches are. Two big aspects factor into that: what's the best implementation for this application, considering only what it needs to do, and what would be easiest for me to implement it in.
00:03:29.640 Because Rails is so easy, I think too many of us try to solve problems on the server side in the Ruby community that would be better solved in the browser. This imbalance leads many Ruby developers to favor Rails too much.
00:03:50.560 I have a provocative statement to make: non-Rubyists are building better JavaScript applications. During my travels this year with Test Double, I attended my first .NET-only conference in Sofia, Bulgaria.
00:04:02.000 I did not expect to have anything in common with anyone there, but all those .NET developers had been building single-page JavaScript applications for years. They all knew Angular and the best Node tooling. We were speaking the same language, and it was awesome.
00:04:25.680 I generally thought .NET was crusty, but the reality was that their server-side framework wasn’t so excellent that they wanted to solve everything on that end. They were making honest judgments about the best places to solve problems.
00:04:42.240 Reflect back on your own career: before Rails, JavaScript wasn’t hard for me. I remember, before Rails, I actually wrote a decent amount of JavaScript. It was only after adopting Rails that my use of JavaScript tapered off.
00:05:01.120 Now, of course, I know JavaScript is a terrible language. But be careful not to conflate that with the notion that writing JavaScript is inherently awful.
00:05:17.120 There's more to complexity than just the language itself. So, let's shift to the topic of planet Earth. One reason I love Ruby isn’t just the language but the community.
00:05:41.600 I love all you guys. I don't even know you yet, but I love you because the Ruby community has changed the world in so many ways. If you were to chart all the great new tools for web application development starting in 2005, you would see gems popping up.
00:06:01.440 By 2006, as time went on, more amazing web tools—best-of-breed tools for building web applications—emerged, not just Rails but also HAML, SASS, and other cool stuff born out of the Ruby community.
00:06:25.680 If you wanted the best tooling, you would look to Ruby. However, I started to notice something around the beginning of 2012: a new browser feature would come out for Chrome, and a gem wouldn’t immediately follow it; instead, it would be a Node package.
00:06:44.800 In 2013, I became inundated with the best new features of the web, primarily being built in JavaScript for Node in terms of tooling. Many Rubyists fear that 2014 could see JavaScript continue to dominate web development.
00:07:09.120 I can't speak to whether that’s accurate, but Gary Bernhardt seems to believe it is.
00:07:20.000 Next, let's talk about what makes the best tools. If you're a new developer trying to find what the best tools are to build a website, there are certain facts that I think we could all agree on.
00:07:39.680 First of all, Ruby's tool ecosystem is mature but crowded. Node's ecosystem, meanwhile, is immature but very innovative. New tools are being added every day.
00:07:50.960 Additionally, tool authors are not immune to trends; they want to go where all the hordes are headed to gain usage. Tools tend to address the problems of their day.
00:08:01.560 So, a tool built as a gem in 2008 may serve the web of 2008 better than the web of 2014. Adding all that together, I'd assert that Node's web application tooling tends to better address today's problems, at least as I see them.
00:08:17.200 This is a sad realization for those who are convinced they only want to write Ruby code. Now, let's explore monolithic application architecture.
00:08:25.760 Rails has won the war of web application frameworks. Before Rails, there were many web application frameworks, but after Rails, everyone was trying to emulate its success.
00:08:37.440 We often got so caught up in Rails's meteoric rise that we didn’t really ask ourselves which specific types of web applications Rails is best suited for.
00:08:46.080 Many of us tend to think of all web apps as equal. DHH, last year at Rails Scotland, gave a great talk about Rails, quoting, "Good frameworks are extractions, not inventions."
00:09:00.480 If we think of Rails as an extraction, what is it extracting from? Well, Basecamp started with the base camp product and extracted the successful parts into something generally reusable, which became Ruby on Rails.
00:09:13.840 What were those components? A solid URL routing scheme, good model behavior, persistence for saving to a database, and managing a SQL schema.
00:09:27.440 It also included good session management for multi-page workflows and a variety of JavaScript alternatives, such as AJAX, ERB tags, and later, unobtrusive AJAX.
00:09:42.080 Most recently came Turbo Links, all developed to avoid having to write raw JavaScript in the client. Why have they developed this way? It’s not that these tools are bad; they just solve a very specific type of problem.
00:09:58.080 They were extracted from Basecamp and aren't generally useful for a fat client JavaScript application—that's not what Rails was designed to address. Basecamp is a traditional document-driven application.
00:10:14.880 If you're building a web application where HTML serves as your UI, it might look simply like an anchor tag.
00:10:24.640 However, this kind of UI programming is declarative. I'm responsible only for describing what the browser should do when clicking that link.
00:10:36.480 I'm not handling all the events in the form itself. This is vastly different from JavaScript UIs, which are event-driven because you're responsible for all behaviors—rendering something onto the page requires code.
00:10:51.840 In a JavaScript UI, if you want to handle a user event, you bind an event to handle it asynchronously. It would be bizarre to think that a single framework would address both types of applications.
00:11:05.840 Just look at the directory layouts! In traditional Rails, we have controllers, models, and views. However, Rails projects often require JavaScript, which ends up in a subdirectory labeled "assets, JavaScript."
00:11:26.080 You'll find that often all JavaScript must go into one large application.js file, leading to a muddled mess for about 75% of Rails applications.
00:11:43.840 Because that mess is problematic, many Rails studios end up duplicating MVC patterns—having a back-end MVC and a separate front-end MVC structure, which can be very difficult to manage.
00:12:03.440 I’ve seen many teams completely remove that traditional HTML UI and create a fresh start with a fat client JavaScript application when it's the best fit for the project.
00:12:16.320 You might see a new developer working on a project with a JSON API back end and a JavaScript UI on the front end. What’s that little connection line there? It's a vestigial appendage.
00:12:31.360 I call it that because we only explain its existence in terms of the past. To understand why that line exists, you need a forensic history of Rails and web application development.
00:12:50.560 What’s wrong with that vestigial appendage? Think about how our brains process small applications when everything fits properly into our head.
00:13:07.680 We can navigate effectively when everything is cohesive and organized. However, as an application grows, every successful application eventually reaches a point where we can no longer grasp all components at once.
00:13:24.640 When that occurs, our brains can only tackle one part at a time. Therefore, if the split is not cohesive, changes in one area may inadvertently affect the other, which could lead to constant context switching.
00:13:40.560 If we had split our applications into two cohesive parts from day one, as the complexity grows, we could grow even larger while minimizing context switching.
00:14:00.960 When we talk about late extraction costs, I like to use the analogy of yarn: if you have two tidy balls of yarn, it's easy to combine them into one tangled mess. But untangling that mess back into two neat balls takes exponentially more effort.
00:14:15.840 In modern fat-client JavaScript applications written in Rails, if you see global JavaScript variables that are data dumps from the server, that indicates your yarn is tangled.
00:14:34.080 You might think you have two applications within one directory, but in reality, you cannot extricate them sensibly. I’ve spent weeks trying to figure out how to make it work without a token.
00:14:53.440 Many deployment concerns crop up, demonstrating that late extraction is more costly than early abstraction. Breaking a monolith into smaller parts is significantly challenging.
00:15:14.080 If we want to move away from monolithic applications and have JavaScript regarded as a first-class language, we must simplify JavaScript.
00:15:30.400 Over the past several years, I've spent a lot of open-source time working on making JavaScript development easier. This process has compelled me to think about what goes into application development and productivity.
00:15:49.920 There’s an application framework layer—models we extend from libraries we use—plus conventions and default configurations dictated by the ecosystem.
00:16:09.360 In Rails, the application framework is obviously Rails's job—ActiveRecord, ActionPack. The conventions and configurations are also determined by Rails, and while Rails does provide build automation via Rake, it's a 'Railsy' version.
00:16:22.720 If I were to grade Rails as an application framework, initially, I found it amazing, but over time, the tangling between my models and the difficulty I experienced with TDD leads me to give it a B minus.
00:16:44.080 However, the conventions and configurations in Rails have always impressed me. I love the tribal knowledge; once you learn a little or hang out in community spaces, you quickly gather all these helpful insights.
00:17:00.560 You can easily ramp up in another Rails project. As for build automation, Rails solves a lot of the 80/20 problems we encounter, but it’s the convention-and-configuration approach I look for in other ecosystems, and I’m usually disappointed.
00:17:17.680 When it comes to fat-client JavaScript apps, the frameworks I want to use seem to change every six months. One moment it’s Backbone, another Ember, then Angular.
00:17:33.680 The slides are actually a bit old, so I haven’t even mentioned React yet. Thus, I don't want to tie myself to one application framework, as that would lead to disappointment.
00:17:44.800 Instead, I prefer to establish conventions and configurations that are framework-agnostic. This brings me to the build automation piece, where I find Node.js particularly appealing.
00:18:01.920 Node is trendy; there's a lot of rapid development occurring in that space. One of its remarkable features is how fast it processes file I/O—20 to 30 times quicker on the same machine than Ruby.
00:18:13.440 Recently, I’ve been working with Grunt, which offers a multitude of community-driven plugins that make task management straightforward. We built a configuration layer called Lineman.
00:18:30.080 Lineman, like a lineman working on a railroad, can be found on Twitter, and we also have a documentation site at linemanjs.com. Let's take a brief look at how to get started with Lineman.
00:18:44.960 To initiate Lineman, you only need to have Node.js installed, after which you can install it globally with an npm command. This provides you a binary for creating new applications.
00:19:02.720 Once you've done that and created a new app, it starts with some cool ASCII art and sample commands to help you get running. You can navigate into your project to see the files that set our expectations.
00:19:23.680 Here, you'll see application CSS, images, JavaScript pages, client-side templates, and a number of configuration files. It's pre-equipped with plenty of tests and a place for vendor files like jQuery.
00:19:38.960 So why does Lineman make life easier? I aim to be able to write code, save it, and have it compile if necessary, ideally in under 100 milliseconds.
00:19:57.920 In Lineman, starting the dev server is simple; it watches file changes and provides real-time feedback. For example, if I change a method to say 'Goodbye, world!' instead of 'Hello, world!', the refresh shows that update immediately.
00:20:11.440 Working within Lineman remains speedy even at scale, which is something we’re continuously looking for.
00:20:23.440 However, it's not merely about testing your application and exploratory development. It's essential that we have a solid TDD process.
00:20:39.920 We ship Lineman with a tool called Testem by Toby Ho, a fantastic test runner. My own test runners have, unfortunately, fallen into disrepair.
00:20:56.480 With Lineman, I can run my tests using an interactive test runner. If I change an expectation, the tests fail immediately, creating quick turnaround times.
00:21:12.080 When it's all set up, it runs tests in Chrome, and I can see failures as I update my code. I can also switch to a CI mode for my test runs, allowing for full build verification on push to GitHub.
00:21:29.600 Lineman projects come with a pre-configured Travis YAML file, streamlining CI setup for front-end projects.
00:21:47.680 Additionally, Lineman’s deployment process is incredibly straightforward. If your server can host static files, you can easily build your front-end web assets.
00:22:04.720 The build command concatenates minified CSS and JavaScript files into a single index.html for ease when deploying. Lineman also incorporates asset fingerprinting for CDN-friendly delivery.
00:22:20.480 We provide a Heroku build pack for seamless deployment as well. Once set up, you can push to Heroku and have everything configured instantly.
00:22:36.000 Various starter projects are available as well. If you're looking to familiarize yourself with Angular, you’ll find plenty of examples available.
00:22:50.080 From there, you can easily move to projects for building standalone libraries or Markdown blogs. Many of Test Double's websites utilize a Lineman blog template.
00:23:04.720 Lineman is also easily extensible; Grunt provides the tasks, and all you need to do is build a thin candy shell around these tasks, guiding their fit into your projects.
00:23:19.760 With this plugin, running `npm install` to save a dependency to your project and add it to your package.json file is a breeze.
00:23:35.760 This specific one is called Lineman Bower; by running it once, you can incorporate Bower for vendor dependency management smoothly.
00:23:51.600 Lineman integrates automatically so you can just run your tasks without additional configuration, effectively tracking every vendor dependency easily.
00:24:07.040 Let’s zoom out a bit. In the JavaScript tool landscape, a lot of tools are packaged as npm modules paired with a Grunt module for task automation.
00:24:22.960 Last night, I stumbled across a Dogescript npm module, which had a corresponding Grunt Dogescript plugin. Naturally, I published a Lineman Dogescript plugin in about 15-20 minutes.
00:24:39.440 Grunt is an exceptional tool—it's user-friendly and provides a great API conducive to building complex, interdependent tasks easily.
00:24:55.440 Returning to the topic of monolithic application architecture: if you are going to break apart a monolith into two applications, you'll end up with a client-side project and a server-side project.
00:25:10.080 These may live in separate repositories or as subdirectories in one repository; it doesn’t really matter. But at runtime, coordination is crucial.
00:25:25.280 Lineman helps facilitate this with a feature called API proxying. Essentially, you set your development browser to point to Lineman.
00:25:37.440 For instance, if using Lineman with a Sinatra app, you would point to port 8000, allowing Lineman to forward API requests to Sinatra. Sinatra responds appropriately, and Lineman forwards results back to the user.
00:25:51.280 To demonstrate this, let me show you how to enable API proxying: modifying default configurations allows API requests to be processed seamlessly.
00:26:07.680 In situations where the server isn’t even developed yet, this severance provides the flexibility to start building the client-side beforehand.
00:26:23.440 We can specify server details later or work on new client-side features without needing to manage any API changes instantly.
00:26:36.960 In those cases, API stubbing allows Lineman to impersonate API calls, letting the browser think it’s getting data from the server when it’s actually returned directly from Lineman.
00:26:50.560 This abstraction enables rapid frontend development while collaborating with backend teams. Let’s illustrate this with an example.
00:27:07.440 In a project, I’ll use Lineman to develop an express application. One simple response handler can return a predefined string.
00:27:23.680 When we refresh, Lineman catches the request due to its priority in middleware, enabling a smooth development experience.
00:27:41.200 This scenario proves especially valuable when clients may first require only frontend work. Everything is handled seamlessly without any extra overhead.
00:27:55.840 Breaking down monolithic applications has other advantages. I had a personal experience with this: I once had a 30-minute test build.
00:28:12.480 When I decided to restructure it into two separate applications, I was curious about the test runtime analysis. Initially, I found that testing the UI alone took only four minutes.
00:28:29.680 Testing the backend—even as a web service—was also quick to write. I discovered that a smoke test on several paths required minimal time to verify.
00:28:46.000 This downscaled my overall test build from thirty minutes to just ten, despite an increase in total net code due to the split.
00:29:10.560 It proved to be an impressive outcome and habit-forming, allowing for further expansions such as adding new mobile applications that communicate with our pure JSON API.
00:29:26.720 Today, I'm excited to announce that we’ve released a Rails-specific plugin for working with Lineman, allowing for seamless integration.
00:29:47.680 It's called Rails Lineman—a gem available in RubyGems—and there’s also a Lineman plugin named Lineman Rails. These allow you to work fluidly between your Rails app and Lineman.
00:30:07.600 By using both projects, during your pre-compile asset stage, your Lineman assets will merge effortlessly into your Rails app.
00:30:22.720 For more information, check out linemanjs.com/rails. We have a cool site designed to help you get started.
00:30:37.360 I'd like to thank a few individuals for their contributions to this talk. My friend Marissa Hile created the illustrations in the presentation. She's a terrific visual designer.
00:30:50.560 Once again, I'm Justin Searls. I'd love it if you tweeted some feedback to me at @searles on Twitter. For any critical feedback, please reach out to our email inbox at hello.
00:31:06.080 I'll tweet a link to this slide deck from my account soon. Thank you very much for your time!
00:32:12.000 Thank you!
Explore all talks recorded at LA RubyConf 2014