00:00:15.519
Okay, so what I'm here today to talk about is modularity. There are two possible talks I could have given here. One of them is sort of a general Ruby modularity talk, and that'll be awesome. But what I am giving today is a talk on some case studies in Rails around modularity.
00:00:20.560
I could go into more depth and give more useful techniques, but instead, I’m going to talk about some specific cases that hopefully will be representative of some things that you could do. I write a lot about this stuff on my blog, so if you want more in-depth information about specific techniques, pretty much everything I'm talking about today, I've written long blog posts about. Feel free to check it out.
00:00:32.880
The first thing I would say about modularity is: you ain't gonna need it. What do I mean by that? I mean that basically when you write an application, there are two ways to approach it. The first way to approach it is to start with a bunch of requirements and then build your application, which can take a year. Some people take a shorter amount of time, but they’re still fundamentally taking time to find those requirements.
00:00:57.920
The way that most of us build our apps is by making a basic plan or a basic idea and then, through short iterations, building our app. In this approach, you gather the requirements from feedback on the actual application, rather than upfront. So, again, there are two ways to do this: the waterfall approach, where you first gather a bunch of requirements, decide what your app is going to look like, and then build it, and the agile approach, where you gather your requirements from the app.
00:01:39.520
A lot of people look at agile and think it's just about doing things faster. It's actually different in that it flips where your requirements come from. Instead of gathering requirements from your brain, committee meetings, or grant letters, they're coming from real users telling you things about your application. Ultimately, it comes down to whether you start with requirements or a basic plan. Agile is also not about not planning; you need to know where you're going. You can't start writing something if you don't know what you're doing, so you do need a basic plan.
00:02:40.160
Let’s consider a real example: the Virtual Observatory architecture. This is a group of individuals who built a standards body to put together some standards for astronomical data. There's this whole system with 17 standards being worked on. Their document for creating the body states that the objective is to improve and unify access to astronomical data and services for primarily professional astronomers, but also for the public. That’s the basic plan they should start with.
00:04:12.640
But if we look at what they've actually achieved, it highlights a significant problem. Recent monitoring indicated that as few as seven percent of registered standard services are fully compliant, illustrating that when you start with a bunch of requirements and create huge architectures, you're not getting enough feedback rapidly enough, and your requirements are coming from the wrong place. Agile allows you to gather requirements iteratively based on user feedback.
00:05:04.479
The modularity should be approached later. If you leave this talk thinking modularity is awesome and decide to spend two weeks figuring out the architecture for every application you build, you will likely end up guessing wrong. It isn’t just about being a good enough programmer; it’s about knowing that it would be wrong to start your application by thinking about the architecture from the get-go. That’s something you figure out later.
00:05:51.680
For example, while Rails came to this party too late, it should not have done so in version 0.5 or even 1.0. Fundamentally, the way you build applications is to start with a plan and constantly iterate until you reach a point where the lack of modularity obviously starts to hinder you. Modularity is all about timing.
00:06:10.399
When we discuss modularity, we're really talking about two things: reducing assumptions in order to increase reuse. Many discussions about modularity focus primarily on reducing assumptions. The reason for reducing assumptions, however, is truly to enable reuse. It's easy to overdo it; modularity should not be pursued everywhere without clear reuse cases.
00:06:40.320
Before I explain further, let me provide an example. Consider a class in Ruby, 'Person', which has a bunch of attributes. If I set up attributes with 'hash.new' and decide later I want to switch from a hash to something else, Ruby allows that flexibility. This flexibility can lead to complicated animals over time, such as alias method chains, which can be effective but are not ideal for building large modular systems.
00:07:48.080
Monkey patching modularity in doesn't fundamentally work, yet many insist it does. This is often seen when developers encounter issues with scalability and performance as the application grows. A good approach is to focus on establishing a basic plan and then building your application iteratively.
00:09:11.440
The first rule regarding modularity is to eliminate global states early on. Tests can be a valuable indicator of where global state creates problems, as they highlight the difficulties in writing reliable reusable tests. For example, you often find that hard coding constants leads to situations where swapping them out becomes impossible.
00:09:54.000
In Rails 2.3, we had issues where everything relied on global state for things like routing. If you set up a system where global constants dictate behavior, you risk creating problematic states that are messy and difficult to manage later. By shifting that dependency to a class-based instance level approach in Rails 3, we can manipulate attributes without affecting the global state.
00:10:57.320
Rails 3 introduced class-level attributes that allow manipulation on an instance level. This means that if a class such as 'ActionController' has a 'perform_caching' attribute, each controller can override that setting without impacting the global state, leading to a cleaner, more manageable application codebase.
00:12:20.240
This method also improves testing since you do not have to worry about side effects of global state across multiple instances of controllers; you'll know exactly which instance's setting you're interacting with.
00:13:06.840
As additional instances retained their state, the rigid class attribute system allowed constructors to set specific values only when needed. Understanding this allows developers to create cleaner and more efficient code that respects modularity principles.
00:13:29.520
The second rule is to embrace object-oriented principles. Many assume allocating memory is expensive when creating objects, but in actuality, it’s quite the opposite in a dynamic language like Ruby. Get comfortable with the idea of objects, particularly when it comes to caching and performance. Objects simplify caching processes that would otherwise take a lot of time in procedural code.
00:14:48.240
For instance, in Rails 2.3, the rendering process relied heavily on procedural methods deeply intertwined with file I/O operations leading to inefficient caching mechanisms. By shifting to an object-based system in Rails 3, we allowed many caching processes to be reused through better design simplifying the stack operations.
00:15:26.720
By creating an object that could be responsible for caching throughout the entire stack, we simplified the interactions and made it easier to optimize performance. Allocating objects remains lightweight compared to the performance gains you can achieve by having a structured caching mechanism in place.
00:16:10.080
Third, resist the urge to hard-code dependencies directly in your classes. Instead, look to use modules that promote better structure and flexibility in your codebase. Encapsulating functionality as modules provides powerful tools for method overrides while keeping the central class implementations clean.
00:17:07.600
You can utilize Ruby’s native module features to create extensible designs where class behaviors can be modified without necessitating convoluted logic changes. This promotes a design-oriented approach that is friendly to modifications later on, which is crucial in a collaborative environment.
00:17:58.560
Using Rails Support Concerns is one effective way to manage inclusion of modules and ensure necessary dependencies are included as well. This can mitigate collisions between various modules by responding appropriately to method calls and avoiding unintended blocks.
00:19:00.080
In summary, everything I’ve shared today is about flexibility—don’t box yourself into hard-coded behavior that limits future functionality. Use objects and modules liberally to create a structure that can adapt and respond to changing requirements.
00:20:05.680
I want to emphasize that this approach doesn’t mean rushing into decisions or jumping into complex architectures without foundational knowledge. Each improvement incrementally restructures the foundation of your application.
00:20:40.960
Thank you for your attention. I hope this discussion has inspired you to rethink how you approach modularity in your own Ruby applications, particularly with Rails. Now, I’d love to open the floor to any questions you may have.