Rails Upgrades

The Long Ball - Upgrading long lived Rails apps from 1.x-4.0

The Long Ball - Upgrading long lived Rails apps from 1.x-4.0

by Jesse Wolgamott

In the talk 'The Long Ball - Upgrading long lived Rails apps from 1.x-4.0' by Jesse Wolgamott at RailsConf 2013, the focus is on the importance and strategies for upgrading legacy Rails applications efficiently. Wolgamott provides insights into the evolution of Rails versions and addresses common challenges developers face while maintaining and upgrading older applications. He suggests a framework for incremental upgrades while emphasizing the risks of staying on outdated versions.

Key points discussed include:
- The Evolution of Rails: Wolgamott tracks the changes from Rails 1.0 to 4.0, highlighting significant updates and features that improve the development experience and application performance over the years.
- Reasons for Upgrading: Upgrading is crucial not only for new features and API enhancements but also for security updates. Legacy apps can be vulnerable if not updated regularly, risking exposure to exploitation.
- Case Studies: He shares insights about organizations like American Airlines that have legacy applications running for decades. The discussion reflects on both successful upgrades and cautionary tales of businesses hesitant to move forward.
- Upgrade Methodologies: Wolgamott contrasts two upgrade methods: a complete rewrite of the application versus a gradual refactoring approach. He contends that organizational support is essential for any large-scale rewrite and discusses the pitfalls that can arise during these processes.
- Practical Upgrade Steps: Specific steps for moving from Rails 2.3 to 3.0 and from 3.0 to 3.1 are provided, including changes to routing, JavaScript updates, and reworking the Active Record interface.
- Security Practices: He emphasizes the transition to Rails 4.0, noting the introduction of strong parameters for improved data integrity and security.
- Focus on Future Improvements: Wolgamott encourages developers to adopt a philosophy of continuous upgrades, ensuring that applications adapt to the evolving Rails ecosystem to avoid stagnation.

In conclusion, Wolgamott advocates for proactive maintenance of Rails applications and careful planning to navigate the complexity of version upgrades. He stresses that while the upgrade process may involve challenges, the benefits of modernization far outweigh the risks of remaining on old versions. A commitment to incremental improvements is crucial for long-term application viability.

00:00:12.259 Thank you! Hi, everybody! My name is Jesse. I live in Texas and have enjoyed Portland a lot. Congratulations to all of you for making it to the end of RailsConf. I always feel like my mind is just bulging at the end of these conferences.
00:00:25.140 At the Houston Ruby Brigade, I like to introduce everyone by having them share when they first started using Rails. It seems like a good touchstone for what you’ve been through and the battles you’ve fought.
00:00:36.960 So, I’d like you to raise your hands if you have used at least Rails 3.2. That should be most of you. What about 3.0? 2.3? Wow! And what about 1.0 or 0.6? Those of you who used DHH, you have the 15 years of experience with Rails that recruiters are emailing you about. Nice job!
00:00:57.780 Most Rails apps don’t last your entire career, right? Most of the time, they’re set aside; they become too old or too much of a pain to maintain. But what if they do last forever? What if they become the legacy apps that we read about?
00:01:14.760 What if they run for 50 years at organizations like American Airlines until they stop running? What do we need to do with these apps so that they continue to provide value, allowing us to maintain and effectively use them? This is a reminder that Rails is not WordPress. You can log into WordPress, regardless of version, and just press upgrade to get to the newest version.
00:01:50.760 Your themes may not work, but Rails isn't set up that way. In Rails, the code is constantly changing, both the internal and external APIs. The ones that we use when we write Rails code are evolving to fit what is considered best practice in development at that time. What do we think is the best way to write applications? Thomas wrote, 'Code is volatile.' If you look at his first Rails commit, he was a Rails core guy but is no longer in Rails. Even if you’re writing the code that is Rails, it’s going to get extracted, removed, or refactored. While this sometimes doesn’t feel great, it’s actually a good thing.
00:02:40.440 If Rails is constantly evolving and getting better, then we, too, are evolving and getting better. If the experience of writing web applications is evolving, then we are evolving. So why would you bother to evolve your Rails apps? First, you get new features; you get the cool API enhancements. If you're creating an API, you can leverage the improved database capabilities with PostgreSQL. These are substantial benefits.
00:03:32.879 Second, there are security updates, which is not a trivial matter. There are Rails 0.8 log engines running out there that, if anyone found out about them, could be exploited with JSON parameters sent to them. So, security updates are paramount to keep your apps online and operational. Additionally, your Ruby language could be at end-of-life. For example, Ruby version 1.8.7 has a very limited amount of time left before it's completely end-of-life, which is not a good scenario.
00:04:01.680 Now, the sort of old Rails app touchstone is a 2.3 application. There are many 2.3 applications still out there providing tangible business value. GitHub is probably the most high-profile Rails 2.3 app right now, and I bet a lot of you have similar applications, which is why you're in this talk.
00:04:45.060 We’re going to discuss a framework that you can use to upgrade your Rails apps, but first, let’s take a step back in time to explore what life was like with a 1.0 app and beyond. Rails 1.0 had a fair number of users, and RJS ruled the world at this point. JavaScript was controlled by your Rails app to update portions of a page.
00:05:12.240 I remember being blown away by this capability. The biggest change at that time was the ability to have Rails apps that would update specific portions of a page and then highlight those changes with flashing effects. Nowadays, that’s not sufficient; we need constant updates based on collaboration. However, back then, it set Rails apart and revolutionized JavaScript interactivity.
00:05:52.919 The code at that time existed in the controller – using `page.replace_html` and referring to the ID and the content you wanted to replace. You would also have `link_to_remote` functions. The interesting part of this architecture was the URL. Your view could direct the controller to execute a specific method simply by specifying the controller name, action, and ID. These would be scattered all over the application. Your route file would likely have a single line resembling `match controller/action/id`, and that would cover everything. Your actions in the controller would check if the request was a post and then make necessary changes.
00:07:26.600 Looking back, you might wonder what we were thinking! It seemed like a dramatic step forward at that time, but now it would be hard to upgrade. Regarding source control, a 1.0 application would often utilize state-of-the-art Subversion. Many older applications might not even be on Git; instead, they could still be on Subversion or, as I recently saw, even CVS.
00:07:44.400 Flashing forward to 2.3, it is widely regarded as the 'holy grail' of Rails applications. Very popular and stable at that point in time, it didn’t even have Bundler set up with it initially. You would define your gems in the application like `config.gem`, and gems, unlike plugins, could be versioned. Gems were a huge improvement over 1.0 because you could specify, 'I want to work with version 1.0 or 2.0,' and if a gem upgraded to version 3, it wouldn’t work.
00:08:29.520 However, specifying gems was optional, leaving some Rails 2.3 apps that do not explicitly declare their gems, simply relying on what happens to be installed on the development or production server. If a gem was required, it was just 'require will_paginate,' and that was it. The Active Record query interface didn’t change much from 1.0 to 2.3; you would still use `find`, followed by `all`, `one`, or `first`, and list the conditions.
00:09:11.040 Essentially, everything was built around the `find` method. Rails 2.3 was nostalgic for many; it was widely regarded as the pinnacle of Rails development by those who romanticize the era. Nevertheless, many developers stopped at 2.3. So what happened when Rails 3.0 was released? This version marked the big merge with larger changes.
00:09:58.140 The query interface changed significantly; instead of defining with conditions, the syntax transformed to use 'where.' This allowed developers to chain together scopes, which started to dominate application design. This evolution was beneficial, especially for new applications, compared to the Rails 2.x era; it felt completely different.
00:10:31.260 Rails 3.0 also introduced Active Model, which provided a new abstraction. This led to many new capabilities in Rails 3 and beyond, making it easier to work with plain Ruby objects in forms. The introduction of Active Model allowed objects to work seamlessly within the framework, enhancing flexibility.
00:11:15.480 Another significant change came with the routing system. While the concepts were still the same, the syntax underwent a complete overhaul. The new syntax is far more elegant, especially compared to 2.3. In Rails 3.1, the asset pipeline was introduced, enabling comments in CSS and JavaScript while combining multiple files into one. The result was improved application performance as it eliminated numerous HTTP requests made to load a single page.
00:11:58.920 However, updating to 3.1 can be challenging, often tougher than upgrading to 3.0 due to the need to integrate all CSS and JavaScript with the new asset pipeline. Luckily, it results in faster load times, as browsers instantly fetch new versions of hashed file names.
00:12:43.560 Rails 3.2 merely improved upon 3.1, delivering faster performance in development environments while also introducing features like `pluck`, which allows developers to retrieve arrays of IDs based on specific queries. During this version, mass assignment became a concern, prompting developers to ensure they couldn’t simply pass every form parameter to the model.
00:13:25.320 This gained attention after a well-documented hack into GitHub, where attackers exploited the GitHub save form to traverse up the tree and gain unauthorized commit rights to Rails. Consequently, mass assignment was addressed with an approach called 'attr_accessible' in the model, specifying which values are safe to change.
00:14:01.680 Now we arrive at Rails 4.0, which brings some pretty impressive Postgres features. If you do one thing today after this talk, I recommend watching Adam Sanderson’s talk from Monday for a deeper understanding of features you are likely already using, which will make you want to upgrade to Rails 4.0. Additionally, Rails 4.0 introduced caching features, such as Russian doll caching and strong parameters.
00:14:45.360 To summarize, Rails 3.1 brought us 'attr_accessible' for mass assignment, and now with strong parameters in Rails 4.0, we see a fundamental shift in how we handle security and data integrity. Strong parameters require us to handle these concerns within the controller rather than within the model, allowing the controller to determine permissible fields for updates to models.
00:15:30.600 Ultimately, Rails 4.0 offers a stable platform that I've found to be the best version in my experience. So, how do we reach that point? There are two main ways to evolve an older application into a newer version. Your users will perceive it as an upgrade to a new version.
00:16:25.360 First, you can undertake a wholesale rewrite of the application, or you can refactor it gradually. A rewrite involves using `rails new`, while refactoring means making incremental updates over time. The decision to rewrite an application is inherently organizational rather than technical.
00:17:10.260 What I mean is that the organization must be committed to the rewrite. If you engage your technical team with the mindset that you want to overhaul the application and eradicate all accumulated cruft, you likely won't succeed unless the entire organization is on board. In my experience, the reality often contrasts with this hopeful approach.
00:18:15.480 The development team often believes they can make better decisions now because they possess more information than before. However, that information doesn't always align with the product owners' perspectives or with what customers expect. Product owners often reject the overridden features perceived as unnecessary, insisting on maintaining certain functionalities.
00:19:12.079 Once the rewrite is defined, the challenges become apparent. Not only must you recreate all previously developed features, but you must also make the new version surpass its predecessor. This raises a significant hurdle when compared to the simpler goal of gradually enhancing the existing version.
00:20:21.600 If you omit a single feature during the rewrite, users may see it as a bug, causing discontent. Additionally, how do you maintain product momentum? If you choose to start a rewrite, you may inadvertently pause development, preventing the release of new features during that period.
00:21:02.400 During the time you've dedicated to rewrites, users will not see any changes, diluting their interest. Users generally do not care about monetary statistics like Rails versions or Ruby upgrades unless performance surges are evident. If you reduce average response time from 500 milliseconds to 250 milliseconds, you may be thrilled, but users typically remain indifferent.
00:21:58.860 While there are certain situations where I would endorse a rewrite—like having a PHP-like approach to Rails where controllers hold numerous methods and models contain none, convoluting the codebase—I would only suggest this if it’s truly warranted. You should take note if your views are excessively complex due to poorly constructed legacy code.
00:22:53.880 If you're utilizing vendored Rails and vendored gems, this can lead to unmanageable situations. It means developers might modify Rails directly, creating a variant I like to call 'Bob Rails' because your application is no longer Rails as intended. If the changes are substantial, upgrading is difficult because you'll override these modifications.
00:23:41.880 If your application has not turned into 'Bob Rails' or a PHP-like scenario, consider refactoring using a series of small wins to progressively bring each part of the codebase into production. You don’t have to jump from 1.2 to 4.0; rather, you can incrementally move from 1.2 to 2.3, then from 2.3 to 3.0 at stable checkpoints, gathering feedback along the way.
00:24:29.520 As you proceed, you build value with each stage while addressing essential upgrade pain points. The primary steps involve migrating from 2.3 to 3.0 and then 3.1 to 4.0. If you're aiming to upgrade from 2.3 to 4.0, you can avoid going through 3.2.
00:25:10.680 As you transition to 2.3, your focus should be on organizing your gems. This means reviewing everything you’ve required in your 2.3 or earlier application and ensuring that’s documented in your gem file, complete with the correct versions. I've noticed that most gem files lack version info, which is problematic when trying to run a `bundle update`. To guarantee Rails 2.3 compatibility, it's crucial to include that information.
00:26:01.680 When moving from 2.3 to 3.0, three main areas require addressing: first, you need to revamp your routing. If your Rails 2.3 app had controller/action/id routing laid out, it’s time to adopt a system that leverages RESTful principles, ensuring it accurately represents the logic of your application.
00:26:47.180 Second, it's essential to rewrite your JavaScript. Many 2.3 applications were created using Prototype and relied on functions like `link_to_function`. Transitioning to unobtrusive JavaScript encapsulates your behavior into a more manageable form, making it easier to manage.
00:27:15.720 Third, you must modify your Active Record interface. Shift from using 'find with conditions' to applying more robust scopes using `Model.where`. Creating class methods that encapsulate your `find` conditions will facilitate this transition and keep your code aligned with best practices.
00:28:06.800 Once you complete these changes and get it all into production, it’s time to celebrate. At this point, you can likely move to Ruby 1.9.0.3. Generally, there are minimal changes required for that transition.
00:28:29.280 As you upgrade to Rails 3.1, the focus remains on reworking your CSS and JavaScript. While I refer to it as rewriting, what you need to do is organize, as many 2.3 and even 3.0 apps frequently had numerous individual JavaScript files one per page. This approach allowed complete freedom in affecting other pages.
00:29:11.280 Now, you aim to consolidate all CSS files into the `app/assets/stylesheets` folder to ensure they work harmoniously with others. If you already utilize asset packaging tools like Jammit, this process will be simpler for you. Otherwise, it's a tedious task of ensuring that your CSS doesn't conflict.
00:30:01.560 The same applies to JavaScript; if their implementation is too broad or conflicting with one another, it may hinder functionality. As you transition from 3.0 to 3.1, ensure that the components behave cohesively by utilizing jQuery in more refined manners.
00:30:49.440 With Rails 4.0, your main objective is to rewrite forms within your controllers by implementing strong parameters. After completing that, you will only need to update your gem file to ensure compatibility.
00:31:15.240 Once you reach Rails 4.0, congratulations! You can also begin preparing for Rails 4.1. In summary, to go from Rails 2.3 to 4.0, include Bundler for 2.3, upgrade to Rails 3.0 with the new query interface, leverage the asset pipeline in 3.1, and update your gem file in 4.0.
00:31:56.040 If you're heavily invested in the Prototype library, there exists a gem called 'prototype-rails' that was extracted from Rails 3.0 and continues to work well. If you prefer not to rewrite or refactor your views, this gem can deliver significant benefits during your upgrade.
00:32:38.460 However, a downside of using Prototype is that it doesn't support no-conflict mode—both jQuery and Prototype want the dollar sign. Luckily, you can turn jQuery into no-conflict mode, but ensure that libraries you utilize also integrate seamlessly within that context.
00:33:24.420 If you're heavily invested in user-generated content, you may want to look at 'rails auto link,' which, while not frequently used, allows you to generate links automatically for user submissions. In terms of deprecated finders, if you prefer not to entirely rewrite your `find_by` queries, you can continue utilizing those deprecated methods.
00:34:02.280 However, these gems tend to last only one major Rails version before they become unusable due to being deprecated. You can fork these gems and maintain them, but that isn't the best use of your time. Instead, focus on implementing new features in your application.
00:34:51.480 To effectively upgrade, always prioritize the future. If you're about to upgrade from a 2.3 app to 3.2, it’s okay to use tools like Prototype Rails to help maintain functionality during transitions, setting up your application for long-term maintenance.
00:35:48.480 Think about the importance of authentication in your 2.3 and earlier apps; password security is paramount as older practices and libraries (such as SHA for password hashing) are no longer viable against modern attack vectors.
00:36:35.880 As you upgrade your authentication systems, consider implementing solutions like Devise, Sorcery, or Authlogic. Unfortunately, this will necessitate emailing users for a password reset, which many may view as suspicous. It's a worthwhile trade-off for ensuring secure password storage.
00:37:20.760 In conclusion, it’s vital to maintain a continual upgrade philosophy. A percentage of your time should be spent keeping up with Rails changes—it's a balancing act. You should aim to install newer features, even within your 3.2 apps, to ensure that upgrades go smoothly as new versions are released.
00:38:30.240 Thank you for your attention, and I hope you found this talk helpful!