00:00:21.500
Today, I am here representing my company, Education SuperHighway, which is based in San Francisco. We focus on improving the broadband situation in U.S. schools.
00:00:52.129
Before we begin, good morning! I’ll try to be funny, as I know that's a crucial part of presentations. To help me with that, I want to start with a famous quote.
00:01:03.329
The quote goes: 'First, make the change easy. If it's hard, then maybe the change is not going to happen.' This made me think about how to make today's talk engaging and humorous.
00:01:19.020
I considered how the presentation feels for first-time attendees. They often don't know what to expect or how to process the information. So, I decided to use emojis to help convey the emotions involved.
00:01:30.470
Now, I've added some emojis into the presentation, and I also love Japanese culture, especially the language and aesthetic. Did you know Ruby is actually a Japanese programming language? One of my favorite bands is also Japanese. If you haven't heard of Babymetal, they are a speed metal band fronted by three teenage girls from Japan—a fantastic combination!
00:01:51.990
But on a more personal note, I own a Shiba Inu, which is the national dog of Japan. Her name is Miki, and I have a picture of her from when she was just a few weeks old when we first got her. This is what she looks like today; she gets so excited when I come home. That's part of the charm I want to share with you all today, along with my love for emojis.
00:02:38.430
I also recently attended RubyConf, where Aaron Patterson made me laugh by airdropping pictures of his cats to everyone at the conference. So, if you have airdrop capabilities turned on, you might receive a few pictures from me as well!
00:03:24.400
Now, I want to address a problem we, as Ruby programmers, might not yet recognize. We have a challenge created by Moore's Law. If you're not familiar, Moore's Law theorized that processing speeds would double approximately every 12 to 18 months.
00:03:35.020
For a long time, this was true, but eventually, this cycle became less predictable. Users began to expect applications to be faster and more complex every year, but the reality is that the processing power needed to keep up with these expectations is not quite there.
00:03:52.480
Consider how hard it would be to write video games today compared to ten years ago; the hardware just wasn't available then. As programmers, we relied on the increase in processing speeds to deliver high-quality products.
00:04:06.190
However, I have some bad news: for Ruby, Moore's Law is essentially dead. More specifically, it's no longer applicable because the focus has shifted from simply faster clock speeds to building more cores into processors.
00:04:32.260
Typically, when I refer to Ruby, I mean MRI (Matz's Ruby Interpreter) because JRuby and Rubinius handle native threads much more effectively. Yet, most usage still revolves around MRI.
00:04:50.080
As an illustration, look at this graph of Intel chip processing clock speeds from 1975 to 2012. You can see that around 2004, the curve plateaued. We hit a wall, and it dropped very sharply from there.
00:05:04.150
This stagnation predominantly happened as we reached our engineering limits. Many of the features on chips are now smaller than some viruses. At these scales, we start encountering unpredictable results and excessive heat, which inhibit further development. Therefore, manufacturers pivoted towards multi-core processing.
00:05:58.990
We have been living in a multi-core world for over a dozen years now, and many developers are looking to functional programming to optimize their code for these architectures.
00:06:12.460
Often, this doesn’t stem from the fact that functional programming is inherently superior for such architectures. Instead, many of the features offer simplified multi-threading and concurrent programming.
00:06:18.360
Some of these helpful features include immutable data structures, delayed evaluation, pure functions, higher-order functions, and curried functions.
00:06:31.660
Fortunately, all of these concepts already exist in Ruby to some extent, and while utilizing these features may be slightly challenging, they are available. For example, we can achieve immutability in Ruby using the `freeze` method.
00:06:52.720
There is also lazy evaluation available with methods like `lazy`, particularly in `Enumerator`. Active Record in recent years has become an excellent example of this laziness in its operation, which allows for improved performance.
00:07:10.740
When looking at pure functions, it's essential to realize that while they may be more difficult to achieve in Ruby due to its natural inclination towards mutation, the focus on designing high-quality implementations is paramount.
00:08:00.930
As for higher-order functions, Ruby excels in this area due to blocks, which serve as an elegant API. Additionally, Ruby includes methods for carried functions in its standard library.
00:08:21.640
However, we still haven't fully embraced these features just yet. I believe Ruby has the potential to remain a language of the future, but we need to rethink how we write our programs. We cannot continue with our current methodologies because that simply won't work as user expectations rise.
00:09:11.290
Users will demand better performance, and leveraging all cores will become necessary to provide that performance. While Ruby Three aims to improve concurrency and integrate a better threading model, it's crucial that we start experimenting now.
00:09:25.590
One of Ruby's greatest strengths is its community, and collaboration is key to innovating future versions of the language.
00:09:39.680
Today, we are going to take a closer look at some Ruby code and refactor it with the future of threading and performance in mind.
00:09:51.150
Let's consider a hypothetical API wrapper for the NFL where we can fetch data about teams or games. We're writing a little command-line interface (CLI) alongside this.
00:10:03.390
First, here's what our CLI script looks like. It straightforwardly fetches data based on the inputs provided—no surprises there. Here are the two public client methods for fetching a team's data and fetching game data, respectively.
00:10:45.810
In the client, we have methods set up to construct the API URL, fetch the raw data, and additional private methods that enhance our data by adding metadata.
00:11:01.990
However, upon reviewing the code, we can see it's not great practice as different responsibilities are jammed into the same class—this should lead us to consider extracting classes.
00:11:18.020
In our new CLI, we would create separate classes for the team data and game data, both inheriting from a base class that abstracts shared methods.
00:11:29.360
This effectively cleans up the hierarchy while ensuring we maintain similar functionality without redundancy. Still, I want to express my hesitations regarding the reliance on inheritance.
00:11:55.640
As classes become more complex, inheritance may lead to tightly coupled code, which can hamper efficiency. Moreover, there's the mystery guest anti-pattern, where class methods could reference data that isn't immediately clear, leading to confusion.
00:12:05.300
Testing becomes difficult when methods are intertwined with hidden dependencies. To address this issue, I advocate for using method objects—which will aid in isolating functionality.
00:12:22.600
Method objects allow us to create clear single-responsibility functions, making our code easier to read and test. Each object would have a single public method, which provides clarity and fortune-telling abilities for debugging.
00:12:57.639
Our extracted classes now become more manageable with single public APIs, and we can easily pass in mocks for testing; this makes the overall code significantly cleaner.
00:13:10.429
Transitioning towards utilizing pure functions can also be beneficial. A pure function consistently delivers the same output for the same input without side effects, making it simpler to test and integrate into applications.
00:13:35.220
Let's quickly evaluate Ruby's built-in methods against these criteria. For example, `IO.read` is not a pure function because it interacts with the file system. On the other hand, basic mathematical functions like `Integer#+` demonstrate pure function characteristics as they deliver consistent results without side effects.
00:14:15.900
As we refactor, it is essential we deliver functionality while maintaining the pure function principle, allowing our main application logic to manage side effects efficiently.
00:14:48.700
By keeping our side effects at the boundaries of our application, we retain flexibility and improve the testability of our code.
00:15:27.330
Let's take a look at how we can implement some of those metadata-adjacent features while avoiding mutation. For instance, returning a frozen hash for shared data allows us to promote state changes without issue.
00:15:55.109
As we advance, naturally, we should strive for maintaining immutability and consider the implications for future Ruby versions using guilds.
00:16:10.600
It's crucial that we start planning for freezing our objects and data as part of our best practices in Ruby, especially in multi-threaded environments.
00:16:44.480
In summary, if there's one core takeaway from today's talk, it's to leverage method objects, which help clarify your code structure and make it significantly easier to manage and test.
00:17:29.290
Dash away mystery guests from your codebase. You'll improve your mental clarity immensely by ensuring you can see everything your file needs without expanding into other contexts.
00:18:00.100
Look for more opportunities to implement pure functions and ensure side effects are confined to a controllable boundary.
00:18:27.820
As we move toward a future of increasing complexity and multi-threading in programming, let's dedicate time to think about how we can enhance Ruby collectively.
00:18:55.570
I believe that Ruby can maintain its relevance and will continue to grow, given our exceptional community. Together, we possess the capability to propel Ruby forward.
00:19:25.720
Let's embrace this and take pride in our shared mission to improve Ruby as a language. Thank you for listening, and I hope to encourage each of you to contribute a little time towards this goal.
00:20:00.860
Let’s engage with the community, collaborate, and never hesitate to bring up our thoughts and ideas for a better Ruby. Thank you!
00:20:30.860
Now, regarding monads—it's not something I regularly implement. However, I am interested in utilizing them within Active Record's attributes API eventually.
00:20:55.900
And of course, there are gems that enhance concurrency and threading in Ruby. While not many seem to actively use them today, ensuring awareness of their existence is essential.
00:21:10.990
It's vital that we begin thinking critically about these aspects to prepare ourselves for future implementations.
00:21:24.350
There will come a time when high-performance, multi-core systems become the standard. As we evolve, Ruby itself might need to adapt some of its architecture to meet those demands.
00:21:50.490
I believe the community will push for these advancements, and it's our responsibility to remain adaptive.
00:22:03.820
As we strive to maintain the joy and elegance of coding with Ruby, we should also prioritize discussions on performance improvements—aspects like enhancing the garbage collection process.
00:22:45.670
In conclusion, thank you all for being here, and I hope you always feel inspired to experiment, innovate, and collaborate. Let’s continue to elevate Ruby together!