Talks

Keynote: The Magic of Rails

Keynote | Eileen Uchitelle

Tropical.rb - The Latin America Rails Conference
https://www.tropicalrb.com/

Tropical.rb 2024

00:00:01.920 Here we go! Hola! That's probably all you're going to get from me in terms of Spanish.
00:00:08.880 I want to switch hands because I'm left-handed. It's a bit weird to hold two things at once, so I have to figure out which hand is better. I hope I didn't confuse the AI starting with 'Hola'.
00:00:33.239 Before we get started, I want to thank all the organizers for inviting me here to speak today. It's my first time in Brazil, and I’m having a lot of fun.
00:00:39.520 Thank you to everyone in the audience for coming. Otherwise, I would just be speaking to Aaron and John, who hear me talk all the time.
00:01:00.480 I’m Eileen Uchitelle, and you can find me online at EileenCodes. I have a blog at EileenCodes.com, which I haven’t updated in a long time. I’m also on Twitter at EileenCodes. I refuse to call it by its new name; it will always be Twitter to me.
00:01:12.799 I work at Shopify as a Senior Staff Software Engineer on the Ruby and Rails Infrastructure team. For years, Shopify has heavily invested in Ruby and Rails, and I’m proud to be at a company that deeply cares about our community.
00:01:27.000 I've been contributing to Rails since 2014 and have been a member of the Rails core team since 2017. The core team is responsible for the future of the framework, and we work together to plan new features and releases.
00:01:46.680 When you've been working with Rails as long as I have, you start to take for granted how the framework is designed and built. From the outside, Rails seems like a big black box full of secrets that only a few know about.
00:02:00.200 Navigating the codebase can feel like traversing a maze of gems, often leaving you feeling lost without a map. While Rails might look chaotic from the outside, it is actually a deliberate, complex, and beautiful framework.
00:02:17.920 It’s not designed to be perfect or solve every use case, of course. Like any other framework, it has flaws, but the premise of Rails is to provide a more complete experience compared to other ecosystems.
00:02:28.680 Rails was built to empower developers and unlock their productivity. Today, we’ll explore the magic of Rails by looking at the philosophy behind the framework and the overall structure of its components.
00:02:40.080 We’ll examine some common patterns that Rails uses to create agnostic and beautiful interfaces, as well as the techniques it employs to hide complexity so you can focus on building your application.
00:03:07.840 By the end of this talk, you’ll feel more confident navigating the Rails codebase and you will understand the patterns it uses to create the framework we all know and love.
00:03:15.159 But Rails is also so much more than its design and architecture. We'll dive into why the community is so important to the long-term success of Rails.
00:03:29.360 So what is Rails? When I ask this question, I'm not seeking the technical definition of Rails as an MVC framework.
00:03:35.480 Instead, I want to know what makes Rails feel like magic. What are the driving principles behind its design, and what sets Rails apart from other frameworks?
00:03:41.680 To start, Rails is modular but not fractured. While the framework is made up of components, we should not confuse modularity with a lack of cohesion.
00:03:58.760 In the early days, Rails was tightly coupled, making it difficult to use one component without relying on the entire framework. When Merb and Rails merged in 2008, it changed the architecture of the Rails framework.
00:04:10.840 From that day forward, we embraced built-in modularity and established clear lines of responsibility between gems.
00:04:24.000 Today, we maintain strict rules on which components can depend on others. For example, ActiveModel only depends on ActiveSupport.
00:04:30.000 We do not accept changes that break this contract or add unnecessary dependencies. We want developers to be able to choose between using all of Rails or just what they need.
00:04:42.360 This empowers Sinatra and Hanami users to utilize ActiveSupport if they desire while ensuring that the framework remains a cohesive codebase by default.
00:04:56.000 Rails is designed to have agnostic interfaces. While the framework provides a set of defaults, users are not required to use the built-in tools.
00:05:06.000 We accomplish this by building interfaces that are agnostic to the tools being used. If you don't like Minitest, you can easily switch to RSpec.
00:05:19.800 If your application requires SQL Server instead of one of the default supported databases, you only need to add the gem and update your database configuration. ActiveRecord will handle the rest.
00:05:32.080 We've implemented similar interfaces for file upload services in Active Storage and job queues in Active Job. These agnostic interfaces require external gems to work with our provided APIs.
00:05:45.000 The upside is that you have the freedom to make decisions about your application. Best of all, you can change your mind mid-development without rewriting all of your existing code.
00:05:58.080 The Rails framework is unique in that much of its functionality has been directly extracted from existing production applications. The very first iteration of the framework came right out of Basecamp Classic.
00:06:11.680 Since then, we’ve upstreamed multiple database behaviors from GitHub. The sharding functionality was influenced by Shopify's monolith, and other features have also been upstreamed from our applications.
00:06:24.760 By building and designing Rails based on the real needs of production applications, we ensure that new features in the framework are stable, resilient, and performant.
00:06:36.000 We test out APIs in our applications to see how they look and feel before they become a permanent part of the framework. This is especially important because once a feature is released, it cannot be removed or changed without going through a deprecation process.
00:06:50.680 We want to avoid unnecessary churn or APIs that might not feel quite right. Extracting functionality that we've already proven in our applications is a great way to build reliable and solid APIs.
00:07:03.680 One thing that's hard to deny is that Rails has aesthetic taste. You might not love the aesthetic, but it's easy to see that Rails aims to create beautiful, simple, self-documenting APIs.
00:07:16.799 The core team spends a lot of time debating the interfaces that applications will consume. We endlessly discuss the right words and how to use them, not because we love bike shedding, but because we want new features to feel like they belong in Rails.
00:07:29.639 The framework's interfaces are not accidental; they're deliberate and focused on look and feel. You might be wondering, though, if Rails cares so much about aesthetics, then why is the codebase so difficult to navigate?
00:07:44.680 Why is it easy to get lost in the framework's internals? The answer is that Rails cares more about your application than it does about its own internals. The framework willingly takes on complexity so you don't have to.
00:08:01.399 To build your application, you don’t need to know how to parse the database YAML or connect to those databases. You don't have to learn how to talk to WebSockets, implement a test harness, or build an image uploader.
00:08:14.919 Why should you need to write the same boilerplate functionality for every application you build? Why should you even have to think about the MySQL command for creating a database table when all you really want to do is get up and running for your future customers?
00:08:28.680 Rails will handle all of this for you so that you can focus on building your application. The way Rails hides complexity is a feature, not a bug. Rails is defined by five tenets, which represent the motivations and goals of the core team.
00:08:41.800 The architecture of Rails didn't happen by accident; the way it's built is a deliberate choice to optimize for the human experience of building web applications. Because of everything Rails tries to do for you, the codebase is incredibly difficult to navigate.
00:08:55.880 There isn't a single person on the core team who understands how every part of Rails works. However, because we all understand the patterns that govern development, we can usually figure out how any part of the framework is implemented.
00:09:08.360 Once you comprehend how it's designed and architected, finding your way around becomes much easier. Rails is made up of 12 components. The first version had just four gems: ActiveRecord, ActionMailer, ActionPack, and Rails High.
00:09:23.679 Out of the 12 current components, four are those original gems, three were extracted from them, and five are brand new additions. The decision to add new components doesn’t happen often; we want to be mindful about what goes in and out of the framework.
00:09:43.640 Since anything we add needs to be maintained by the core team, we have to be careful. While it sounds like we have one person per component, it doesn’t quite work out that way.
00:09:58.440 Everyone on the core team has an area of expertise, but none of us are the sole owner of any particular component. Some of us have overlapping expertise.
00:10:15.760 One frequent topic in conversations about how Rails is designed is the significance of naming conventions. Why are some gems prefixed with 'Active' and others with 'Action'? And why doesn’t Rails Tie even follow that convention?
00:10:29.640 The libraries prefixed with 'Active' generally support backend behavior, including ActiveRecord, ActiveSupport, ActiveModel, ActiveJob, and ActiveStorage. These components take care of things behind the scenes.
00:10:46.080 The naming convention was initially influenced by the ActiveRecord pattern, which ActiveRecord is named after. It made sense to use the same prefix for other backend components.
00:10:58.480 DHH wanted all of the gems to follow a similar naming convention, so 'Action' followed 'Active' to signify user-facing behavior, where a user makes an action.
00:11:08.840 These include ActionMailer, ActionPack, ActionView, ActionCable, ActionText, and ActionMailbox. The rules for naming components can be a bit blurry.
00:11:18.960 You could argue that ActionCable is not as user-facing as this diagram implies, but hardly any name is perfectly representative.
00:11:29.440 Lastly, the component that ties everything together is called Railties. A railroad tie is one of the cross braces that supports the metal rail on a railroad track, which is why Railties doesn’t follow the 'Active' or 'Action' naming conventions.
00:11:44.680 Railties defines the Rails constant and serves as our entry point into an application. While Railties are the core of the framework, none of the gems in Rails depends on it.
00:12:03.360 This allows you to use the components of the framework you want without committing to using all of Rails at once. The modularity of Rails is a conscious choice to give you the freedom to use only what you need.
00:12:17.680 Rails implements various common patterns and techniques to build the interfaces and APIs that applications will consume. Ruby on Rails makes heavy use of factory patterns, inheritance, metaprogramming, and a series of hooks and callbacks to control the load order of components and their configuration.
00:12:31.440 Navigating the Rails code can be quite challenging because it takes on a lot of complexity in order to provide consistent interfaces and simple APIs.
00:12:47.680 However, the techniques that Rails employs enable the framework to provide an application development experience unmatched by any other framework.
00:13:01.120 Let's first look at how components interact with Railties to get a better sense of the role it plays in initialization and how it ties the framework components together.
00:13:14.880 The entry point from an application to a component is Railties. Any gem that needs to hook into the initialization process will contain a file called railtie.rb. This is where we define the load hooks for the framework.
00:13:31.040 These load hooks apply application configurations and settings. Additionally, the config/environments and config/initializers directories are how your personal application hooks into Railties and those components.
00:13:46.720 Not every component implements a Railtie; it depends on whether that gem needs to extend or modify the initialization process. External gems that require interaction with initialization and boot, as well as configuration, will also provide a Railtie.
00:14:01.240 Think of a Railtie as an API for hooking into Rails during the boot process. The Railtie functionality in a component can only be used within a Rails application.
00:14:17.920 For instance, if you’re using ActiveRecord with Sinatra, you’ll need to apply configuration yourself. This means that some things that simply work out of the box with Rails need to be manually implemented if you are using a component outside of the framework.
00:14:34.080 Let’s look at an example of how Railties hooks work. When you boot your Rails application, Railties will register the load hooks for each component from the defined initializers.
00:14:54.000 It then waits until each component is loaded, and once it’s done loading, the hooks that were registered before the application booted will run.
00:15:09.160 In general, Railtie initializers are run in the order they are defined. Therefore, any code in your application is going to run last, after the Rails initializers.
00:15:26.000 This functionality also makes boot time faster by enabling lazy loading of Rails application components. Let’s take a look at some code to see how this works in practice.
00:15:40.640 If we look at the railtie.rb file in any of the components, we’ll see that they define a bunch of initializer blocks. If we examine the method definition in Rails, we can see that these initializers take two arguments: a name and an options hash.
00:15:58.960 Technically, it takes three arguments because there’s a block, but you don't really pass that. The name serves as simply an identifier, and the only options that are accepted are 'before' and 'after'.
00:16:10.840 These can be used to control the order in which the initializers are run; however, they are rarely set as we want the initializers to run in the order they are defined.
00:16:25.120 But 'before' and 'after' can be useful if one component's initializer needs to run after another's. Initializers can take a block that returns the application we’re working on.
00:16:38.720 This can be used to interact with methods defined on the application object or access the config object, as seen within these initializer blocks.
00:16:52.800 You'll often see an 'on_load' hook that tells Rails to register an action but wait until a specific component is loaded to call the code inside the initializer.
00:17:06.640 On-load hooks enable the ability to apply configuration lazily by registering a hook that is called when the library is loaded.
00:17:22.880 The initializers are often used to set up the required functionality as the application boots. For example, if you’ve ever used ActiveRecord outside of Rails, you’ll need to call 'establish connection' yourself.
00:17:38.560 However, in a Rails application, the connections are already established for you. So, where does that initial connection happen? In an initializer, of course.
00:17:50.240 If we look in the railtie.rb file and ActiveRecord, we can find the initializer responsible for establishing connections to ActiveRecord base.
00:18:01.440 When we boot our Rails application, the name of the initializer is 'active_record_initialize_database'. Because we have an on-load hook, the initializer will not run until the ActiveRecord component has finished loading.
00:18:14.960 When this code runs, it first locates the database.yaml file from the application’s path and parses it. It will then pass the parsed result to ActiveRecord base's configuration setter.
00:18:28.640 This process converts the YAML into database configuration objects. Once the database configurations are set, 'establish connection' is called, and that's it; this is how Rails sets up and establishes the initial connection.
00:18:45.240 Establishing this connection for you is just one of the many things that Rails does when you boot your application. Initializers also help us avoid calling components if they aren’t included in a Rails application.
00:19:00.400 Here’s an example of how this works: in this initializer, we can see that ActiveRecord will include the ControllerRuntime module only if ActionController is loaded.
00:19:14.400 It will include the JobRuntime module only if ActiveJob is included. If an application doesn’t have ActionController or ActiveJob, these modules will never be included.
00:19:27.720 This approach prevents applications from having to depend on either of these components. While these are simple examples that fit on my slides, there are many more complex examples of initializers in Rails.
00:19:40.960 Next time you’re curious about what Rails is doing during boot, open one of the railtie.rb files in a component to find out.
00:19:56.599 The important takeaway is that Railties are the core of the framework, linking your application to each component. Most major components of Rails will implement a Railtie to apply configuration and set up the application during the initialization process.
00:20:13.760 Without Railtie initializers, we would need to perform a lot more manual setup in our applications. Railties help handle configuration and initialization so you don’t have to think about all of these moving parts.
00:20:27.920 Rails uses initializers with on-load hooks to control the load order and ensure that hooks are run at the correct time. This prevents a component from being loaded too early and is one way we ensure Rails configuration doesn't override application configuration.
00:20:42.080 Finally, Railties enable components to interact with each other without creating dependencies. Because the Railtie waits until a component is loaded to call its code, we can define behavior for when a particular gem is available in an application.
00:20:56.560 For example, as we saw earlier, ActiveRecord will only include the JobRuntime module if ActiveJob is loaded. If it's never loaded, we prevent unnecessary dependencies that force you to use ActiveJob.
00:21:10.480 While we only looked at a couple of examples, this barely scratches the surface of the power of load hooks. The next time you're wondering where configuration is coming from, look in the railties components for the answer.
00:21:26.080 I have to switch hands; my arm is going to sleep. This thing is heavy—it feels like a good five pounds.
00:21:40.400 Earlier, we discussed how Rails provides agnostic interfaces, which enables you to swap out default functionality as long as external tools implement the interface Rails provides.
00:21:58.560 One example is how ActiveRecord database adapters are implemented. By default, Rails supports four database adapters: PostgreSQL, MySQL2, Triscale, and SQLite3.
00:22:13.680 Triscale is a new adapter for MySQL that was built at GitHub, and it's available in Rails 7.1 if you want to check it out.
00:22:23.520 It has no dependency on the MySQL client, so you shouldn't face too many issues building it from source.
00:22:36.520 If you look at the ActiveRecord codebase, you’ll notice that we never directly call `is_a?` on the database connection adapter.
00:22:51.440 This is because the database adapters are structured such that their interface is agnostic to the database client being used.
00:23:04.000 We achieve this by creating an abstract adapter that implements the API, with all concrete adapters inheriting from that abstract adapter.
00:23:17.920 The abstract adapter defines the interface; you should never interact directly with the abstract adapter in your application. Instead, you should only use one of the concrete adapters.
00:23:30.960 Each concrete adapter inherits from the abstract adapter and thus defines the same interface through inheritance. The default behavior for each method is stored in the abstract class.
00:23:44.320 The concrete adapters, including those external to Rails, must inherit from the abstract adapter to receive that interface and implement the same public APIs.
00:24:06.040 The pattern we use to generate agnostic interfaces means that in Rails, we can call a method like `supports_foreign_keys` without needing to perform an `is_a?` check.
00:24:18.840 All adapters respond to the same methods. If a concrete adapter wishes to have different behavior from the default, it can simply reimplement the method to alter that behavior.
00:24:32.320 For example, here we have the definition of `supports_foreign_keys` for the abstract adapter; by default, it is set to false.
00:24:48.520 If we examine the PostgreSQL adapter, we can see that this method is reimplemented and set to true. The Triscale, MySQL2, and SQLite3 adapters also define this method and set it to true.
00:25:04.280 The abstract adapter sets it to false because we can't guarantee that external adapters, which we don't control, will support foreign keys.
00:25:15.920 This creates an interface where foreign keys are optional for any database adapter within Rails.
00:25:30.560 Another area where we use a similar pattern to create agnostic interfaces is ActiveStorage. ActiveStorage implements this in a slightly different manner compared to ActiveRecord.
00:25:44.680 Here we have a class called 'Service' that is equivalent to our abstract adapter. It serves as the abstract class defining the interface, but instead of providing default behavior, it raises a 'not implemented' error.
00:26:01.680 This indicates to external gems that are implementing an ActiveStorage adapter that they must define this method for their service to function with Rails.
00:26:15.760 If we look at the Google Cloud Storage service, we can observe the implementation for the delete functionality. Each ActiveStorage service must implement this method in their concrete classes.
00:26:30.380 Where the abstract service raises a 'not implemented' error, the agnostic interfaces allow Rails to simply call 'service.delete' with the key, regardless of the service being used.
00:26:48.560 No checks are needed; Rails will call these methods, and the service will respond accordingly. This pattern reduces code complexity and clarifies what the interfaces are.
00:27:05.120 As a summary, Rails utilizes abstract classes and inheritance to implement agnostic and consistent interfaces for the services we support.
00:27:18.560 It also provides an easy way for library authors to create their own service or adapter. Rails implements an abstract class which all concrete classes inherit from.
00:27:30.640 The abstract class defines the interface while the concrete classes are responsible for modifying behavior by redefining those methods.
00:27:46.160 You will see this pattern throughout the Rails codebase wherever we need to implement a pluggable interface. One of the advantages of this pattern is it simplifies the Rails framework by avoiding excessive 'is_a?' checks.
00:28:01.280 We can assume that any call to an adapter or service will implement the defined interface. This also prevents undefined method errors for any external adapter or service, enabling them to fall back to the preset behavior.
00:28:16.320 In case they need to define that method, they can also opt for a 'not implemented' error.
00:28:30.560 In addition to simplifying Rails, the patterns we use for creating agnostic interfaces facilitate the swapping of adapters in applications.
00:28:44.760 If you start your application with SQLite, you can easily switch to MySQL2 by updating the Gemfile and database configuration. This also applies to ActiveStorage services.
00:29:01.520 If you prefer not to use Google Cloud, you can easily switch to Azure by simply updating the service configuration.
00:29:12.880 These interfaces allow us to provide sensible defaults without locking you into what we recommend. Lastly, implementing a standard set of interfaces for external providers reduces the maintenance burden on the core team.
00:29:27.280 We need not understand how every available service works; we can provide the ones we're comfortable maintaining while allowing others to build what they need.
00:29:42.560 Rails is a large framework managed by a small team, so it’s crucial that we do not take on more maintenance burdens than we can handle.
00:29:57.440 Agnostic interfaces implementing a stable API for external gems help us avoid supporting countless providers while not restricting you to our default options.
00:30:06.960 In addition to the patterns we've explored, Rails heavily uses metaprogramming to achieve simple and beautiful APIs.
00:30:18.560 This can sometimes make it challenging to identify where behavior is defined in the framework. Much of what you think of as Rails magic is driven by metaprogramming.
00:30:36.960 While tracing code that utilizes this technique can be difficult, it is fundamental to building APIs that are both beautiful and conceal complexity.
00:30:49.760 There are numerous examples of metaprogramming in Rails, but today we’ll focus on one example that illustrates how we use class_eval to build the associations' getter and setter APIs.
00:31:01.760 Let’s say we have two models, Post and Comment. A post has many comments, while a comment belongs to a post. This is a straightforward example.
00:31:16.880 Have you ever wondered how we can call `post.comments` without explicitly defining this method in our model? How does the post know about comments at all?
00:31:30.880 ActiveRecord can’t anticipate which methods you're going to add to your model, so it doesn’t have a predefined `comments` method.
00:31:45.760 Figuring out where this method is defined can be tricky, but when I face such challenges, I use Ruby's Source location method. This tells me the filename and line number where any method is defined.
00:32:01.360 We can find the definition of `comments` by calling the `method` method on the post and passing `comments` as a symbol. Then, by calling `Source location`, we get the necessary information.
00:32:17.040 If Source location returns nil, it indicates that the method is defined in a C extension or C Ruby itself. In this case, the `comments` method is defined in association.rb on line 103.
00:32:36.960 If we open that file, we find it points to a method in ActiveRecord's Association class called `define_readers`. This method accepts a mixin and a name.
00:32:50.320 Rails calls `class_eval` on it. If we don’t understand how this metaprogramming works, we might think that the mixin here is the post model class.
00:33:03.520 However, it’s actually a dynamic module named `PostGeneratedAssociationMethods`, which is dynamically created when the post model is loaded.
00:33:20.080 This module is where Rails defines interface methods for the Post model. This is critical because the magic behind Rails comes from dynamically generated methods, classes, or modules when you boot your Rails application.
00:33:37.440 Back to `define_readers`: inside the class_eval block, a method is defined using the association name, in this case, it’d be `def comments`.
00:33:57.440 ActiveRecord looks up the association from the association class and then calls the reader method. This allows Rails to provide an API to access comments on post without forcing you to implement your own comments method.
00:34:13.040 The reader method has a similar definition, but it calls `writer` and passes a value. You’ll notice that these method definitions are closely related but implement slightly different instance behavior.
00:34:28.560 Additionally, you’ll see that `class_eval` passes a magic `__FILE__` and `__LINE__` variable. This is a feature in Ruby that provides the file and line number for a metaprogrammed API, making debugging easier.
00:34:48.600 This capability ensures that Source location works correctly. Numerous methods are dynamically generated when dealing with associations in Rails.
00:35:02.440 My objective is that by illustrating one small part of metaprogramming to build one simple and beautiful API, it will seem less magical when you're working with an association in the future.
00:35:14.960 Association accessors are just one example of how Rails uses metaprogramming to provide beautiful, simple, and clean APIs. Without this functionality, you would have to write your own queries and define your own comments getter and setter.
00:35:30.600 That doesn’t sound fun, does it? Rails doesn’t want you to handle all that work; we aim to provide you with sensible, self-documenting, simple APIs that simply work.
00:35:47.440 This is the beauty of Rails and the source of a lot of the so-called magic. We use metaprogramming techniques throughout the framework, such as `method_missing`, `class_eval`, `instance_eval`, and `define_method`.
00:36:02.440 Metaprogramming allows us to develop interfaces for migrations and generators. So many features that you associate with Rails are super powerful, but they also complicate Rails' internals, making them harder to navigate.
00:36:18.320 The trade-off is that you enjoy beautiful APIs, and you don’t have to think about how these methods are generated; Rails just manages this for you.
00:36:31.160 If all this is new to you, you’re not alone. When I first began building Rails applications, I didn’t know how any of the Rails internals worked.
00:36:46.960 I had no clue how Rails was architected, the role that Railties played, or how we used metaprogramming. I had no appreciation for the design and construction of Rails.
00:37:02.960 During my early days working on the framework, I was often lost and frustrated, but studying Rails internals was exhilarating.
00:37:20.600 For me, Rails is more than just a framework; it provided me with a career and a community. Rails isn’t just code or the patterns we discussed today; it's greater than its individual components.
00:37:38.680 Rails is inspiring. It introduced us to convention over configuration to help eliminate boilerplate code, popularizing the MVC pattern alongside Django.
00:37:56.320 Rails inspired the Elixir Phoenix framework and PHP's Laravel framework. Even if you aren’t a fan of Rails, which I hope you are, it’s undeniable that Rails has influenced modern web development today.
00:38:16.960 Rails is not only inspiring; it’s empowering. It aims to make you productive, removing tedious issues like scaffolding, migrations, and database connections.
00:38:31.720 Rails builds agnostic interfaces to provide sensible defaults without locking you into our recommendations.
00:38:46.560 We ensure that Rails is modular, so if you don’t want to use everything, you are empowered to use just the parts you like.
00:39:03.520 Do you prefer Sinatra but still want ActiveRecord? Great! You have that freedom. Want to write a simple script but love the datetime helpers from ActiveSupport?
00:39:19.520 You can do that too! That’s why we have a video showing how to build a blog in 15 minutes. Rails is designed to enable you to build quickly and efficiently, keeping complexity out of your way.
00:39:34.960 Rails is imperfect. I've spent a lot of this talk praising Rails and all that it does for you, but I can't say it’s without flaws.
00:39:50.320 Rails aims to be a framework that meets 80% of users' needs. It doesn't try to handle every possible scenario; instead, its goal is to enhance productivity by providing most of what you need.
00:40:06.720 Rails is also imperfect because it's 20 years old. It’s no longer an unruly teenager; it's a mature and stable framework.
00:40:22.080 However, this maturity comes with imperfection. We can’t simply change functionality if applications depend on how it currently operates.
00:40:36.960 Rails is imperfect because the people working on it are imperfect as well. We make mistakes, add features we thought would be beneficial, break builds and applications, and occasionally create security vulnerabilities.
00:40:50.880 But imperfection is simply part of being human. Rails also represents the applications we build, from small businesses to large enterprises.
00:41:05.160 Rails powers many of the applications we interact with daily. There are thousands upon thousands of applications around the world using this framework, and they represent just one aspect of what makes up Rails.
00:41:19.360 Rails also includes the team behind it. As a maintainer of Rails, my goal is to see the framework evolve, ensuring the community of contributors continues to grow.
00:41:35.920 The maintainers are responsible for ensuring that releases occur regularly, features get merged, and we assist new contributors, answer bug reports, and help out on Discord.
00:41:54.160 Without the 12 core team members and 27 additional maintainers, we wouldn't have Rails as it exists today. In the years since its open-source release, over 6,000 people have contributed to the framework.
00:42:09.760 Every single person who has made changes to the framework is a part of Rails. And of course, Rails is about the community; it's not only the core team but all of us.
00:42:23.440 We make the community what it is. Without a community of users and contributors, Rails would merely be Ruby code.
00:42:38.560 But we all know Rails is so much more than the sum of its parts. It’s about human-centered design, agnostic interfaces, and beautiful APIs that arise from genuine applications.
00:42:56.560 Rails inspires and empowers developers. It's imperfect and flawed, comprising the applications we build, the team behind it, and the community.
00:43:15.960 It may be a bit sentimental, but I genuinely believe that Rails is magic. The role each of us play in Rails' development community and its future is what makes it magical.
00:43:29.760 It’s up to all of us to continue building this framework and this community so that new people will join us and those who are here will stay.
00:43:43.360 Connecting with people is the reason we are all here; it is what makes it worthwhile. I'm grateful to be back at conferences, meeting new people, and seeing old friends.
00:43:59.760 We may have different goals, but we all care about the same thing—Ruby on Rails. I hope you will join me in being an active and present member of Rails' future,
00:44:13.520 whether that is through conferences, blog posts, mentoring, or contributing. Let’s work together to ensure this framework and this community are something we want to keep returning to.
00:44:31.680 I have to switch hands again—this is heavy! Thank you.