The blog of , a Ruby on Rails development team
Known from Rails LTS, makandropedia and Nanomize

We still know basic Rails development

Last year we successfully launched the PURA App, a medium-sized project for a medium-sized enterprise. PURA is a commercial cleaning company based in Neusäß, with a catchment area of all southern germany.

Due to its size (the company has about 1400 employees), PURA faced some organizational challenges. Managers easily lost track of which objects needed their support. Heaps of paper checklists were stacking and increasingly hard to manage. PURA’s quality management was depending on the oral reports of object managers, as was their customer satisfaction feedback.

They needed a lightweight solution that would combine all these object management activities into a single system. So we worked out the requirements with them, then designed a solution and implemented the PURA App.

Simple Rails, Powerful Technology

The PURA App is a simple Rails 4 application with four basic models, supplemented by a handful of nested models. Each of the basic models has a standard CRUD. It has authentication and simple authorization. So far a greenfield project.

However, we used some exciting new Gems. katapult took the cumbersome project start burden off our shoulders. After about half an hour of application model definition, katapult implemented the model as basic, yet fully runnable application that could already manage users, objects, visits and even had specs and feature tests. From this raw workpiece we crafted the final application with all its domain-specific twitches and specialties.

A second major innovation was the employment of upjs, a Javascript library that enhances applications with server-generated content. Instead of moving the whole application into the browser, as Angular, Ember & co. do, it brings a clever system of content preloading and substitution, vastly improving the speed and responsiveness of the application.
While still writing a good ol' Rails application with Rails controllers and views, we created an application that is as snappy as previously only pure client-side apps could be. And if a browser has Javascript disabled, up.js will graciously degrade, leaving the user talking to Rails with no effort.

While object managers track cleaning quality and customer satisfaction with their tablets on site, the company's management coordinates and evaluates the customer care from the backoffice. So we added a simple role system with Consul and optimized the application for usage on tablet and desktop, as these are the main interfaces. As a result, the application has a well-structured, lucid interface that adapts to the user's role and the size of the display.

Fully operational

An often-overlooked part of creating a web application is the need to host it when development is done. Our long-standing experience in hosting more than 50 Rails applications convinced PURA to trust us, and their reliance has paid off: In more than half a year they've had no downtime, while any disclosed vulnerabilities in either Rails or Unix got fixed instantly. That's what we call rails complete.

Still down to earth

Over the years, our portfolio has grown into all areas of web development. From simple single-purpose web applications we've expanded to a huge content management ecosystem for Audi, a highly-available Smart TV controller for Pro7, a portfolio of various APIs for another customer, a large statistical data evaluation system and many more.

Compared to these, PURA's challenges were simple. That's why it took us less than two months to build an application that serves their needs just right, helping them to elaborate on their services to their own customers.

Surviving the upgrade pace of Rails

This post is an excerpt from our e-book Growing Rails Applications in Practice, now available through The Pragmatic Programmers and Leanpub.


In contrast to languages such as Java, the Ruby on Rails ecosystem has little tradition of keeping APIs backward compatible. Hence, new versions of Rails and Ruby gems often break existing code, making an upgrade very time intensive.

Not upgrading is not an option either. Once you get too far behind the latest version of Rails you will not receive any further security updates. Given the severity of some of the vulnerabilities that have been disclosed in the past, you probably do not want to expose an unpatched Rails application to the open Internet.

There are products like our own Rails LTS that offer security patches for old versions of Rails. However, if you want to take advantage of the latest features, you will find yourself locked in an eternal rat race of upgrading your application dependencies every few months.

This chapter wants to give some advice for dealing with this sitation.

Gems increase the cost of upgrades

When adding a new gem dependency, consider the cost of upgrading that gem through the lifespan of your application. Will the gem's author still be interested in maintaining the library two years down the road? When push comes to shove, would you be willing to replace that gem or take over maintenance if there is no version compatible with a new version of Rails?

Be aware of different upgrade costs between libraries that provide low-level abstractions and those that offer highly coupled mini frameworks. For instance a library that supplies geographical calculations might not even have a dependency on Rails and is unlikely to ever break during an upgrade. Whereas a gem that dynamically generates an admin backend for your application will almost certainly break after a new Rails release due to its many hooks into the internals of Rails.

Upgrades are when you pay for monkey patches

Monkey patches (or "freedom patches") describe the practice of opening up an existing class from a gem dependency and overriding some method with custom behavior. While this can be a way to quickly move on when encountering a fatal library bug, monkey patches are usually the first thing that break when upgrading Rails.

Be careful when monkey patching the internals of a class that does not belong to you. You will pay for it later.

If you find a bug in a gem that you like, consider forking the gem, committing your fix with a test and creating a pull request to the original author. This way your monkey patch can soon be replaced with the next official version of the gem. It also makes for good karma.

Don't live on the bleeding edge

There is no need to upgrade to a new version of Rails on the day it becomes available. Wait until a major Rails release has matured into a couple of patch levels before upgrading, i.e. don't upgrade to Rails 6.0.0 right away, but wait until Rails 6.0.3 is published.

This also gives authors of your other gem dependencies a chance to update their library to integrate with the new version of Rails.

We are alive!

If nobody is publishing to this blog, this usually means we are quite busy. Indeed, we worked on several exciting projects over the past months:

  • We re-launched Audi MediaCenter last week which has been a challenging task. We're happy it's online now. We also do hosting for Audi - our operations team reports 100% uptime so far.

  • We sold our job platforms around webentwickler-jobs.de to ICTJOB Deutschland GmbH.

  • We re-launched the website of our friends at ARTEMIS Group.

  • We worked on a HbbTV project for ProSiebenSat.1. It's fun to see your Ruby code broadcasting to several hundred thousand TVs.

  • We welcomed Emanuel as trainee while Judith is now working as software developer after completing the trainee program.

So what's next?

  • We're planning for the upcoming Rails LTS release which will provide security patches for Rails 3.2. In case you're on Rails 3, you might want to migrate to Rails LTS.

  • We're going to do some secret consulting project for an exciting new customer that many of you know. We're really looking forward to this.

  • We'll have barbecue during summer. If you're around Augsburg, drop us a line and join!

While we have plenty to do until the end of the year, hopefully we'll manage to post here from time to time. We're also preparing to launch a shiny new product in late summer. I'm sure you will read about that on our blog.

Organizing large Rails projects with namespaces

This post is an excerpt from our e-book Growing Rails Applications in Practice, now available through The Pragmatic Programmers and Leanpub.


As a Rails application grows, so does its app/models folder. We've seen applications grow to hundreds of models. With an app/models directory that big, it becomes increasingly hard to navigate. Also it becomes near-impossible to understand what the application is about by looking at the models folder, where the most important models of your core domain sit next to some support class of low significance.

A good way to not drown in a sea of .rb files is to aggressively namespace models into sub-folders. This doesn't actually reduce the number of files of course, but makes it much easier to browse through your model and highlights the important parts.

Namespacing a model is easy. Let's say we have an Invoice class and each invoice can have multiple invoice items:

class Invoice < ActiveRecord::Base
  has_many :items
end

class Item < ActiveRecord::Base
  belongs_to :invoice
end

Clearly Invoice is a composition of Items and an Item cannot live without a containing Invoice. Other classes will probably interact with Invoice and not with Item. So let's get Item out of the way by nesting it into the Invoice namespace. This involves renaming the class to Invoice::Item and moving the source file to app/models/invoice/item.rb:

 class Invoice::Item < ActiveRecord::Base
   belongs_to :invoice
 end

What might seem like a trivial refactoring has great effects a few weeks down the road. It is a nasty habit of Rails teams to avoid creating many classes, as if adding another file was an expensive thing to do. And in fact making a huge models folder even larger is something that does not feel right.

But since the models/invoice folder already existed, your team felt encouraged to create other invoice-related models and place them into this new namespace:

File Class
app/models/invoice.rb Invoice
app/models/invoice/item.rb Invoice::Item
app/models/invoice/reminder.rb Invoice::Reminder
app/models/invoice/export.rb Invoice::Export

Note how the namespacing strategy encourages the use of service objects in lieu of fat models that contain more functionality than they should.

Real-world example

In order to visualize the effect that heavy namespacing has on a real-world-project, we refactored one of our oldest applications, which was created in a time when we didn't use namespacing.

Here is the models folder before refactoring:

activity.rb
amortization_cost.rb
api_exchange.rb
api_schema.rb
budget_calculator.rb
budget_rate_budget.rb
budget.rb
budget_template_group.rb
budget_template.rb
business_plan_item.rb
business_plan.rb
company.rb
contact.rb
event.rb
fixed_cost.rb
friction_report.rb
internal_working_cost.rb
invoice_approval_mailer.rb
invoice_approval.rb
invoice_item.rb
invoice.rb
invoice_settings.rb
invoice_template.rb
invoice_template_period.rb
listed_activity_coworkers_summary.rb
note.rb
person.rb
planner_view.rb
profit_report_settings.rb
project_filter.rb
project_link.rb
project_profit_report.rb
project_rate.rb
project.rb
project_summary.rb
project_team_member.rb
project_type.rb
rate_group.rb
rate.rb
revenue_report.rb
review_project.rb
review.rb
staff_cost.rb
stopwatch.rb
task.rb
team_member.rb
third_party_cost.rb
third_party_cost_report.rb
topix.rb
user.rb
variable_cost.rb
various_earning.rb
workload_report.rb

Looking at the huge list of files, could you tell what the application is about? Probably not (it's a project management and invoicing tool).

Let's look at the refactored version:

/activity
/api
/contact
/invoice
/planner
/report
/project
activity.rb
contact.rb
planner.rb
invoice.rb
project.rb
user.rb

Note how the app/models folder now gives you an overview of the core domain at one glance. Every single file is still there, but neatly organized into a clear directory structure. If we asked a new developer to change the way invoices work, she would probably find her way through the code more easily.

Use the same structure everywhere

In a typical Rails application there are many places that are (most of the time) structured like the models folder. For instance, you often see helper modules or unit tests named after your models.

When you start using namespaces, make sure that namespacing is also adopted in all the other places that are organized by model. This way you get the benefit of better organization and discoverability in all parts of your application.

Let's say we have a namespaced model Project::Report. We should now namespace helpers, controllers and views in the same fashion:

File Class
app/models/project/report.rb Project::Report
app/helpers/project/report_helper.rb Project::ReportHelper
app/controllers/projects/reports_controller.rb Projects::ReportsController
app/views/projects/reports/show.html.erb View template

Note how we put the controller into a Projects (plural) namespace. While this might feel strange at first, it allows for natural nesting of folders in in app/views:

app/
  views/
    projects/
      index.html.erb
      show.html.erb
      reports/
        show.html.erb

If we put the controller into a Project (singular) namespace, Rails would expect view templates in a structure like this:

app/
  views/
    project/
      reports/
        show.html.erb
    projects/
      index.html.erb
      show.html.erb

Note how two folders project (singular) and projects (plural) sit right next to each other. This doesn't feel right. We feel that the file organization of our views is more important than keeping controller namespace names in singular form.

Organizing test files

When we have tests we nest the test cases and support code like we nest our models. For instance, when you use RSpec and Cucumber, your test files should be organized like this:

File Description
spec/models/project/report_spec.rb Model test
spec/controllers/projects/reports_controller_spec.rb Controller test
features/project/reports.feature Cucumber untegration test
features/step_definitions/project/report_steps.rb Step definitions

Other ways of organizing files

Of course models/controllers/tests don't always map 1:1:1, but often they do. We think it is at the very least a good default with little ambiguity. When you look for a file in a project structured like this, you always know where to look first.

If another way to split up your files feels better, just go ahead and do it. Do not feel forced to be overly consistent, but always have a good default.

Rails LTS: Supported version changes

Rails LTS is a commercially supported fork of Ruby on Rails that provides security patches for legacy Rails releases. Today we're announcing some changes in the versions that we plan to support in 2015 and beyond.

Rails 2.3

We will continue to support Rails 2.3 indefinitely.

Rails 3.0

When we announced support for Rails 3.0 earlier this year, we had hoped to create a stable customer base to fund long-term maintenance of Rails 3.0. Unfortunately we haven't been able to gather much interest for Rails LTS 3.0 despite our best efforts, so with a heavy heart we're announcing plans to sunset support for this version.

In order to give 3.0 users sufficient time to upgrade, we're going to support Ruby on Rails 3.0 for another full year until January 1st, 2016. After that date we will no longer be able to provide security patches for Rails 3.0.

Rails 3.2

We plan to support Rails 3.2 when official maintenance ends entirely, sometime in 2015 (Rails 3.2 still receives limited maintenance for severe security issues). We could already win an enterprise customer for this project, which allowed us to start working on 3.2 support.

Rails 3.2 support will be available to all Rails LTS customers on a Startup, Standard or Enterprise plan.

Our address:
makandra GmbH
Werner-von-Siemens-Str. 6
86159 Augsburg
Germany
Contact us:
+49 821 58866 180
info@makandra.de
Commercial register court:
Augsburg Municipal Court
Register number:
HRB 24202
Sales tax identification number:
DE243555898
Chief executive officers:
Henning Koch
Thomas Eisenbarth