Gregory Brown

Where is Ruby Really Heading?

Help us caption & translate this video!

http://amara.org/v/GUQK/

GoRuCo 2009

00:00:19.520 All right, I have to admit I was a little bit scared putting together this talk. I'm going to try my best to answer the question of where Ruby is really heading. However, what I hope to do is provide you with a way to answer it for yourself. Hopefully, this won't turn into a religious talk. I'm not going to tell you there's one true right way. I hope you agree with me that Ruby is diverse enough that we don't need one true right way. This also won't be a super scientific talk, so if you're looking for hard numbers on different aspects, you might be getting something different than expected. Really, it's a talk about choices—both choices I've made and choices we, as developers, will need to make moving forward when working in Ruby.
00:01:04.479 I'm definitely not a fortune teller, so please do not come to me a year from now and tell me I was wrong about some of these things. I know I will be. I'm also not a language implementer, and up until a couple of months ago, I didn't really know much about language design. Please don't argue with me over who has the fastest Fibonacci sequence; this isn't that sort of talk. Instead, I'd like to focus on a few stories about things I've actually dealt with in my projects.
00:01:30.640 Now, the most well-known project of mine is Prawn. How many people have heard of or used it? Okay, great! Prawn is interesting as a case study of Ruby because we decided from the get-go that we would develop it on Ruby 1.9, and this was before Ruby 1.9.1 was released. We wanted to make sure to maintain support for Ruby 1.8 at all times; it would be considered a bug if it did break. There are some interesting aspects to discuss regarding that decision.
00:02:10.160 Another interesting point was the decisions I needed to make when writing this book, which will be out in a few weeks. O'Reilly might have given you all free rough cuts to access or something like that, so you might want to check it out. If you think about it, a year ago, deciding whether to talk about Ruby 1.8 or Ruby 1.9 was a non-trivial question to answer. I will also talk briefly about some work I've been doing in my commercial projects because, even though I tend to do a lot of open-source work, I just happen to work for a very understanding company, which leads me to encounter some interesting problems.
00:02:48.800 Much of this is going to be about forks in the road where you have to decide which direction to go. This decision will significantly affect how things progress in your projects. I'd like to start with Prawn. With Prawn, we were attempting to build a PDF library that would be significantly faster than PDF Writer, support multilingualization, and allow for a high level of expressiveness in document building.
00:03:02.560 At the time, the promises of Ruby 1.9 seemed obvious. After all, it is made out of cheetahs that speak every language, blessed by a magical wizard with magical features. However, while writing Prawn, I needed to replace PDF generation in existing software that was running on MRI Ruby 1.8.6. This meant that despite Ruby 1.8 being slow and somewhat outdated, it was still like that old pickup truck that, while rusty and dated, had done a lot of good work and couldn't just be discarded.
00:03:39.400 As it turned out, it took about 80 or 90 percent of the effort to take Prawn, which was written on Ruby 1.9, and bring it back to Ruby 1.8. Now, this was a bit of smoke and mirrors because we planned this from the beginning; we intentionally used features that allowed it to run on both systems. I'd like to talk a little about how this process works and how you can do this in your projects if you want to support multiple Ruby versions.
00:04:03.040 One thing that's great is if you have a greenfield project—something you're just starting from scratch—getting it to run on Ruby 1.9 isn't that difficult. However, it's also not going to be automatic. For example, trying to print out some Unicode text in Ruby 1.9 will not work correctly; you'll get an error message stating there's an invalid multibyte character.
00:04:30.239 The solution to this problem is relatively easy: every source file you write needs to have a magic comment specifying the encoding of that file. This may seem tedious, but it actually has benefits. In Ruby 1.8, strings are essentially sequences of bytes and nothing more. Character-based access and manipulation are possible but require workarounds. Conversely, in Ruby 1.9, you work with characters directly, and if you want to manipulate bytes, you need to be particularly cautious.
00:05:08.920 An interesting consequence of this is that even if you believe your application will only need to work with American English, if you're doing any form of binary manipulation, you must remain aware of this. This could be as simple as loading an image file and manipulating it. When you're dealing with strings or I/O objects that contain binary data, you need to be explicit to ensure Ruby interprets it correctly; otherwise, it could lead to bugs.
00:05:58.679 For instance, Ruby doesn't look at the default external encoding for your files based on the locale on your file system unless you explicitly set it. I encountered all sorts of strange bugs in Prawn because I didn't realize this situation existed. It happened when I was trying to interpret some files as UTF-8. Because Ruby 1.9 is smart about encoding, it knows how to validate encodings. This means that if you have a binary string that includes invalid Unicode characters, and Ruby interprets it as Unicode, it will cause issues when running your code.
00:06:38.960 Thus, the concept is rather simple: to ensure your code works the same everywhere, you must be explicit about the file's encoding. For text files, this includes setting the character encoding. For binary files, you can use methods like `binread` or open the file using the 'b' flag. Many developers used to think that unless their software needed to run on Windows, encoding didn't matter, but this is not only a line feed issue; it's an issue of how Ruby 1.9 interprets that code.
00:07:15.320 Everyone must understand that when working with binary files in Ruby 1.9, it's critical to pay attention. A beneficial side effect is that developing predominantly in a Unix environment can help catch bugs related to this before releasing your code, which will make Windows users happier. Multilingualization is going to confront you when you start working with Ruby 1.9, and as a consequence, you can no longer treat a string as an enumerable. You can't just map across lines; you need to be more explicit about what you're working with.
00:07:57.679 This means using enumerators or iterators available in Ruby and directly telling Ruby whether you want to work with lines, bytes, or characters. Once you adopt this habit, it's not too challenging to incorporate. These issues impeded my progress when I first started working with Ruby 1.9, as I had no previous experience, only blog posts telling me about the exciting features. I couldn't continue to develop Prawn until I learned about these features. It depends on whether you're building something solely for 1.9 or if you need backward compatibility.
00:09:11.200 Be aware that certain syntax features are new in Ruby 1.9, and if you choose to use these, back-porting becomes more complicated. You can't simply isolate them by version; instead, you need to isolate them on a file level to avoid them being parsed by Ruby 1.8; otherwise, your code simply won't run. Now, what I'd like to do is take this general idea of how you can get started with Ruby 1.9. I believe this is sufficient for getting your code running on Ruby 1.9 today if you wanted to.
00:09:40.480 However, what if you don't have a greenfield project? What if you have a side project that you want to test with 1.9? You can use the same ideas mentioned but may need to approach it differently. Now, I'll briefly outline a rough process for transitioning Ruby 1.8 code to 1.9. This is essentially what we did in Highline, a command line interface library.
00:10:12.960 First, you need to ensure you have decent test coverage. You can use a tool like archive for this, which works on Ruby 1.8, but it doesn't work on 1.9. The important point is that before migrating your code, you must ensure you have tests in place; otherwise, it will become complicated when you begin making implementation changes. This is common sense but easy to overlook, leading to problems when you're unsure if a bug was introduced accidentally or due to something inherently wrong in 1.9.
00:10:56.960 If you want to upgrade something, get your tests up to speed first. In Prawn, testing has become difficult due to the complex output generated in PDFs. We utilize an excellent library called pdf-reader to analyze this output, but we don't test every single feature because if pdf-reader doesn't support it, we must create our own parsers for those elements.
00:11:30.560 We created numerous examples for testing. We devised a rake test to automate these examples, ensuring it captures any errors and informs us of any breakage. If you're transitioning code from 1.8 to 1.9, having this in place is invaluable. If dealing with a Rails app, it might be as simple as setting up a fitness test, but if you're working on a library or script, write up use cases and make sure to hammer through them while you're working on this stuff. This will help you catch subtle errors that your tests might miss.
00:12:01.920 Once you have these two things in place, you have a framework for beginning experimentation. I recommend, regardless of your version control system, creating a branch dedicated to Ruby 1.9 modifications. Work on it until your tests pass. At this stage, avoid using new syntax; don't stress about supporting both Ruby 1.9 and 1.8 simultaneously. Just focus on getting the tests to pass and examples to run successfully on Ruby 1.9. After that, you can evaluate whether you plan to continue supporting 1.8 in the future.
00:12:50.560 If you have an open-source project, this may be a sensitive subject. If these are scripts running on your machine or projects for clients, you may not need to keep supporting Ruby 1.8. As a developer, maintaining parallel implementations across versions is burdensome, and supporting multiple versions can constrain what you can do.
00:13:37.680 I recommend you think long and hard about whether you will continue support for Ruby 1.8 after you have your stuff working in Ruby 1.9. Personally, some of you may want to keep that old workhorse around for certain projects. But the process is straightforward: get your tests to run on 1.8 without breaking on 1.9. For Prawn, we were very specific about the versions we supported, only Ruby 1.8.6 and Ruby 1.9.1—other versions may work, but we don't support them.
00:14:01.680 To achieve this compatibility, we simply run test suites for both versions, checking against Ruby 1.9 as we do so. You should also look into ZenTest for the multi-version Ruby utility. This is an excellent tool that enables you to run multiple Ruby versions concurrently against your tests, alerting you immediately if something breaks even if you haven't been using those versions in your day-to-day work. This allows for easy access to multiple Ruby installations.
00:14:42.960 Rather than implementing hacks to ensure something works on both Ruby 1.8 and 1.9, if the features are straightforward to backport, it might be better to proceed that way. If you want to discontinue support for an earlier version of Ruby, avoiding the need for any future breakdown of versions will be easier if your code was already designed to meet a specific specification.
00:15:23.199 Whenever possible, we try to do this safely by checking if features already exist to avoid conflicts in different implementations or versions. I wouldn't advise doing this for anything overly complex; however, if your needs are reasonable, it’s a good approach. Another tip is that when checking actual version numbers, it can be helpful to create a utility which enables you to selectively run code blocks based on which version you're working with.
00:15:51.760 This can greatly clean up your code and make it easier to modify how these elements work internally across versions. The key is, when back-porting a Ruby 1.9 library to 1.8, try to isolate the changes you make so they can easily be seen by other developers using your software or by yourself in the future. This ensures that if something breaks, it's much easier to fix. Hopefully, by doing these things, you'll be back to greener pastures.
00:16:32.960 This is the experience I gained from Prawn, and what I want you to take away is that if you follow these steps, you'll be able to start working on small scripts or any projects in Ruby 1.9. Try it out! I intentionally didn't cover the new features or speed comparisons, as most of this depends on your specific use case. For instance, when working in Prawn, if you're embedding a lot of images, YARV is incredibly fast for that due to the number crunching it requires. But if you're primarily manipulating text with transcoding from various encodings, it may run slower than MRI, and it's vital to keep that in mind.
00:17:10.560 Moving to Ruby 1.9 isn't a magical solution to every issue. It may perform exceptionally well in some scenarios while being less advantageous in others. The best way to determine this for yourself is to experience it firsthand—you should give it a go, as it can be beneficial in many situations.
00:17:55.440 Now, I'd like to switch to discussing the possibility of developing a book about which versions of Ruby should be in use. By choosing to make Ruby best practices centered around 1.9 only, it sends a strong signal of confidence in Ruby 1.9 as a forward-moving direction. Interestingly, this perspective was prompted by David, a technical editor for one of my books. He raised an issue I hadn’t considered: if discussing Ruby 1.8, what does that entail? It is quite simple to draw a line between Ruby 1.8.6 and Ruby 1.9.
00:18:42.560 Ruby 1.8.6 is the reliable workhorse capable of running legacy software. Most of you likely have experience writing code on MRI for Ruby 1.8.6. Now, what does Ruby 1.9 represent? It’s seen as the bridge to Ruby awesomeness, a magical future. The real question is—where does that leave us with Ruby 1.8.7? To answer this, I'll quote directly from the documentation, as those changes serve as core references.
00:19:20.240 The core changes in Ruby from version 1.8.6 to 1.8.7 comprise some method signatures being added or entirely new methods being introduced. However, the standard library was more conservative during this shift; there is a valid question regarding the impact of these changes. Although backports may seem cool, relying on them does not guarantee that code written for Ruby 1.9 will function correctly on Ruby 1.8.7.
00:20:06.000 One thing is clear: if you depend on Ruby 1.8.6, using even one new feature introduced in Ruby 1.8.7 may lead to compatibility issues, causing your reliable old truck to 'explode'. This poses an interesting challenge for project maintainers, compelling them to visually assess each patch submitted to their repositories to ensure compatibility.
00:20:51.840 In doing so, maintainers risk addressing compatibility issues arising from external code changes. Meanwhile, package maintainers face their own hurdles when determining which Ruby version to ship. Most distributors are moving toward adopting Ruby 1.8.7 as the standard, yet many users find Ruby 1.8.6 to be preferable due to its stability. The backlash against updating to Ruby 1.8.7 arises from such experiences. Consequently, authors must deal with the nuances of specifying guidance on the right versions of Ruby to best serve their projects.
00:21:35.040 Upon writing this book, David and I faced concerns with Ruby 1.8.6 experiencing too many security problems and instability, evidently making the situation quite difficult. The downside is telling a newcomer to Ruby, 'Go to this FTP site and download this exact patch level'—a burdensome directive that leaves them struggling to get started. Ruby 1.8.7 essentially stands as a transitional release; discussing it alongside Ruby 1.9 yields differences that shouldn't be ignored.
00:22:04.240 Ruby 1.8.7 offers users a choice to gradually explore new features, giving them the option to migrate to 1.9 as they find necessary. However, this process creates a dilemma for those caught between 1.8.6 and 1.9, as some legacy applications require significant maintenance while still needing to ensure compatibility with new developments. The conclusion persists that adopting Ruby 1.9 is increasingly seen as the most viable long-term strategy.
00:22:56.160 I wanted to pause for a moment and gather feedback from the audience regarding their experiences. Anyone who has encountered this decision-making—whether you're a maintainer or simply someone exploring the language—may feel free to share your experiences now. I'd love to hear your thoughts.
00:23:30.960 Eric Cottle here, if you don't know me. I maintain RubyGems and various other projects. Personally, I think I'm going to begin moving away from supporting Ruby 1.8 entirely because there are just too many versions to maintain smoothly. That said, it's going to take me over a year to accomplish this. I'm not rushing this process and realize there are still many people utilizing 1.8.
00:24:13.760 In fact, I witnessed that many continue to backport features from Ruby 1.8.7 to 1.8.6, meaning some features may still work if you’re targeting 1.8 but I won’t concern myself if it breaks now, especially for older documentation like RubyGems. As time moves forward, you should consider which version to support. It's fascinating how certain changes can break a lot of things, yielding a broader conversation.
00:24:51.919 Discussions often arise regarding whether it's possible to code specifically for 1.8 while still being compatible with 1.9. The consensus appears that while it’s feasible, it's important to note which functions exist in 1.6 versus 1.7 or even earlier versions. Issues do arise with compatibility, as demonstrated by the struggles faced when switching between these versions. And indeed, it's easy to become caught up in the details when attempting to keep straight what everything allows or doesn't allow—especially with how feature implementation varies from version to version.
00:25:51.119 The categorization of features presents a significant topic. Historically, various preceding versions displayed inconsistencies in running code when upgrading, and with 1.8.7, many users may experience similar effects that hinder their development workflow when attempting to shift to Ruby 1.9. For many, the absence of a smooth upgrade process reflects a lack of trust in the current versioning scheme. Decisions made for Ruby up to that point may influence how developers choose their respective versions moving forward.
00:26:20.160 Additional friction arises as users begin post-1.9, attempting to migrate existing applications to the newer version while considering compatibility between all instances of Ruby present in their working ecosystem. Yet as an author documenting this process, I question how to clearly outline these many nuances. As we develop additional projects, we must pay special attention to the decisions made within the community and how adoption diverges across differing implementations.
00:27:06.560 On a different note, I have encountered amusing situations with the implementations I've worked with. In my company, we mostly engage with UNIX systems, benefitting from Ruby's general compatibility across various POSIX distributions. However, we occasionally must operate on Windows systems. I find this situation to be somewhat comical in some instances yet frustrating overall! Currently, we are building an API on top of a truck routing software that exclusively runs on Windows and possesses a C API solely for Windows.
00:27:47.760 The reality is, we need to create a bridge between Ruby and this C API, integrating FFI (Foreign Function Interface) to minimize dependencies on C extensions. Fortunately, FFI enables wrapping C libraries using pure Ruby, eliminating the need for compilation steps which can be cumbersome. This flexibility opens doors, allowing us to call a collection of functions without complication. The resulting implementation is straightforward and functional, making the process far more admirable.
00:28:27.919 Until then, my takeaway is that the implementation process, especially when leveraging FFI, has been effective and has provided solutions for operating systems where pure Ruby offers less compatibility. To add to this FXI reduces the difficulty of implementing layered layering while keeping complications at bay. Ultimately, Ruby solved the dilemma surrounding OS-level integration quite admirably, even if this interpretative system precariously bridges different functionalities.
00:29:07.680 Now, I’d like to wrap up with an additional personal thought. Developing with multiple Ruby implementations is truly an enlightening experience. The implementations I've examined exhibit varied qualities; for instance, standard Ruby 1.8.6 acts as the backbone for many applications, easily integrating with third-party libraries due to its well-known nature.
00:29:52.280 In contrast, Ruby 1.9, particularly YARV, focuses on speed and comprehensive multilingual support. Its innovative features provide a fresh outlook on app performance. However, navigating and determining the right version to use can be confusing for newcomers, particularly when assessing the support available for earlier versions. As developers continue to build libraries for Ruby, the perspectives added across each subsequent version become increasingly significant.
00:30:35.520 I challenge all developers to explore alternative Ruby frameworks such as JRuby or Rubinius further. Each presents remarkable advantages that many users may not yet appreciate fully. Ensure your exploration also includes an insight into Ruby 1.9’s capabilities as a next step; it's essential to remain informed about these possibilities.
00:31:12.400 Finally, initiating the Ruby Specs Project proved fruitful too, allowing various libraries to align with implementation and version specifications. This offers improvement and collaboration across our community while maintaining awareness of different versions and their upcoming features.
00:31:50.480 The project aims to clarify and document Ruby thoroughly, which will be mutually beneficial for developers transitioning across versions. If anyone is interested in learning more or contributing, feel free to reach out and join the initiative.
00:32:37.920 Can you share your thoughts about Ruby 1.9 coexisting on machines? As recently as a couple of weeks ago, there were notable issues with the snapshots for Ruby 1.9 marked with version 1.8. If installed, it disrupted libraries built for 1.8. How are you managing your systems now in terms of version control?
00:32:59.920 I've managed this by ensuring that each Ruby installation maintains its distinct environment. Whenever I install a new version, I keep paths separated to avoid collisions with the previous installations. An interceptor script can help gauge compatibility and performance when checking various implementations. It’s a tedious yet reliable method, ensuring each environment remains compartmentalized without conflict.
00:33:44.480 Ultimately, this approach puts the user in a position to replicate the same thoroughness in organization necessary to optimize for Ruby's development future. I'm open to hearing more about your inquiries and footing various conversations on the Ruby community. Let’s stay connected as we continue to navigate the changes and challenges ahead.
00:34:35.040 Let me remind you all to engage with the Ruby specifications project in whatever way works for you. It's a fundamental resource that can provide crucial insights while allowing you to understand Ruby's evolution. So contact me through social platforms, and thank you for your time today!