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!