00:00:20.810
All right everyone, let's get started. This is my talk, "Sprinkles of Functional Programming." Before we begin, I would just like to express a few thank yous.
00:00:26.400
First off, thank you very much to Ruby Central for inviting me to come speak today. I'm super thrilled to be here and to be part of this amazing Rails conference.
00:00:39.180
Secondly, thank you all for coming and joining me in this first hour of the conference. I'm super excited about this material, which has added a lot of benefits to my programming practice over the past couple of years.
00:00:46.470
I'm excited to be able to share it with you. So I'm John Schoeman. I work at Thoughtbot as a developer and consultant.
00:01:00.059
When I first told some of my colleagues that I had a talk accepted into the RailsConf, the first thing they said was, "All the best RailsConf talks have a Twitter slide." It turns out I don’t have a Twitter, but I still made you a Twitter slide. That is my real GitHub account if you want to find me on the internet, and I just go by my name, John Schoeman.
00:01:20.580
Cool, so let's just dig into the talk. My goal for this talk is to convince all of you that the ideas and styles of functional programming are worth considering and integrating into your Rails practice. This could lead to numerous benefits in terms of qualitative code and the effectiveness of developers.
00:01:41.369
A roadmap for this talk will start with establishing a thesis and key points right at the get-go. We'll talk a little bit about functional programming and object-oriented programming at a very high level. Different people tend to mean different things when they say these terms, and they can be a bit ambiguous. I’m going to explain what I mean when I use them so we can all be on the same page.
00:02:05.099
I’ll make a recommendation that will include a story with some code. That’s where we will spend most of our time today. Then we'll recap and end with an action item for you to take home.
00:02:30.020
One thing to note that’s not included in this roadmap is that I am not planning to talk about implementation details of how to write functionally styled code on a line-by-line basis. So if you have zero exposure to functional programming, that's totally okay. We won't delve into those details, but we will spend most of our time at a much higher level talking about why and when you might want to consider functional programming.
00:03:01.220
At the end, there will be a slide with some resources if I’m successful in inspiring you to learn more on your own. Good starting points for you. Cool.
00:03:36.360
So let's get to the thesis. Hopefully, these are points that we can all agree on. First off, Ruby is a general-purpose programming language and a multi-paradigm programming language. It is excellent at both object-oriented and functional programming. We, as developers, get to choose which style to use.
00:03:54.170
As Rubyists, I'm sure we're all very familiar with concepts such as classes, inheritance, defining methods, polymorphism, and encapsulation. But we also have access to things like lambdas and the Enumerable module. Ruby 2.5 introduced `yield_self`, and Ruby 2.6 has further enhanced data pipelines, making building them even more convenient.
00:04:06.880
The second point is that different paradigms lend themselves to different tasks. Specifically, object-oriented programming tends to be very good at modeling behavior, whereas functional programming is generally better at handling data. Based on these two points, I argue that as Rails developers, we should choose our programming style based on the task at hand, as doing so will lead to a better product.
00:04:40.070
To elaborate, if you're modeling behavior, you should prefer classes and objects. However, if you are handling data, you should leverage data pipelines and functions.
00:05:00.000
Next, let's discuss functional programming versus object-oriented programming. I think most people focus a lot on objects and functions when discussing these paradigms. This is a great way to categorize programs based on their style. However, in my day-to-day development practice, I have found it more useful to consider what aspects of the different styles are difficult to change.
00:05:35.750
To illustrate this, let’s consider how these styles handle changes in requirements. In an object-oriented program, adding a new behavior (a new method) is very straightforward. You can just add that method without needing to change existing code.
00:06:02.399
In contrast, adding data in object-oriented programming can often require refactoring existing code. In functional programming, the opposite is true. Adding new data is typically easy, while introducing new methods may require changes to your code.
00:06:30.630
Some typical use cases for object-oriented programming are anything with modeling in the name, such as users, posts, and interactions between entities in your business domain. These are nouns that you can point at. On the other hand, functional programming is typically used for data manipulation, such as data transformation or aggregation, where you take input data from one point and get it to another.
00:07:17.000
This talk was inspired by multiple instances during my career, specifically in relation to code that I have written and code written by others. By applying these concepts regarding what is easy or difficult to change, I have found that doing so has added a lot of value and resolved many pains.
00:08:00.000
Based on my experiences, I've distilled some recommendations that I would like to share. Given that methods are easy to add in object-oriented programming while data is easier to add in functional programming, we should consider how we expect requirements to change before starting a task.
00:08:21.000
This particularly includes considering what users might want in the future, even though that's a difficult question to answer. If we do that and select our development paradigm accordingly, many good outcomes will occur.
00:08:41.000
To illustrate this, I will provide a story set in a fictional company, where I outline a series of future requirements. I'll first solve them using an object-oriented approach and then revert to a functional approach, creating a choose-your-own-adventure story.
00:09:04.000
At the end of this tale, we'll compare and contrast the results to derive any lessons learned. Additionally, there will be plenty of code, and I will be going through it quite quickly. I encourage you not to read it too deeply, just a squint test to catch anything important. If you're trying to reach 100% comprehension, that might be unreasonable.
00:09:27.000
I will provide a GitHub repo link at the end, so if you want to review the code in detail afterward, it will be available.
00:09:51.000
Let’s imagine a Rails app—a typical sort of Rails monolith. It's a product-oriented business with eight developers on the team and a product manager practicing Agile methods.
00:10:03.000
For the sake of the story, let’s say this is a B2B application that allows small businesses to upload their inventory, which users can review and purchase. Think of it as a mix between Craigslist and a product catalog.
00:10:21.000
Currently, users can come to a products index, fill out forms to create products, and life is good. The company is doing well and gaining new clients regularly.
00:10:40.000
The sales team is currently negotiating with a new client, the largest one they've ever had. However, this new client has concerns about uploading their products, as they have a significant volume and need to change it frequently.
00:11:02.000
They ask if they can import their internal CSV inventory directly into the system. The sales team agrees to this request, sets a deadline, and submits it to the product manager.
00:11:29.000
The work gets added to the product roadmap and incorporated into the next sprint. The developers start working, and they successfully end up with a pull request that allows users to upload product CSVs.
00:11:44.000
Considering their company practices test-driven development, they begin with a high-level feature spec that allows the user to attach a CSV file and upload it. The implementation involves iterating over each row of the CSV, massaging the data where necessary, and saving it to the database.
00:12:09.000
They end up with an imports controller and additional specs. Here's a GIF demonstrating the demo to their new client, who is thrilled.
00:12:26.000
Weeks pass, and everyone is happy: users are purchasing products, and the company is thriving. However, the sales team is soon negotiating with an even larger client, who has a different approach to data management.
00:12:49.000
The new client asks if they can import Excel files, as they do not work with CSVs internally. The sales team reassures them that they can.
00:13:05.000
The developers start working on this new requirement, and first notice that it might be sensible to separate the imports from the product model. They decide to create a service object called Product Data Importer, which will handle this concern.
00:13:26.000
With this architecture change, they will be instantiating a Product Data Importer and passing the file path to its constructor.
00:13:41.000
They notice another opportunity for separation of concerns: pulling out the formatting of the data handling process. Thus, they introduce a new service object called Product Data Formatter.
00:13:56.000
Now they have a cleaner structure, and updating the code to handle Excel files involves simply adjusting the file extension check.
00:14:11.000
Progress continues smoothly, and various commits allow for new features and enhancements. However, they encounter another client who has a different currency format.
00:14:27.000
They need to handle dollar signs in front of their prices, which necessitates more updates and refactoring. The developers continue to refine their approach, maintaining a focus on separation of concerns.
00:14:47.000
As they introduce a dollar sign formatting feature, they maintain test-driven development principles and end up producing a lot of service objects for separation of concerns.
00:15:10.000
As they wrap up, there's a balance of okay performance and client satisfaction, leading to high coverages of their test suite.
00:15:31.000
Now let's compare how all of this would work in a functional programming scenario. Imagine the same team attended RailsConf and were inspired by a talk focused on functional programming.
00:15:48.000
They decide to approach the client requirements with a functional mindset, knowing that behaviors remain constant while data requirements change. They want to separate the importing behavior into a single function.
00:16:06.000
The first thing they do is create a service class that only exposes one public method called import, which accepts a file path. The transition is smooth because they can maintain their existing specs while altering their approach.
00:16:24.000
They quickly set up their data pipeline and separate I/O logic from data processing. This allows them to easily add the ability to import data from Excel without altering any preexisting methods.
00:16:45.000
They ensure their implementation is clean and readable, which in turn helps with testing down the line. With each requirement, their functional approach allows for simple updates, like adding dollar signs to prices.
00:17:07.000
As they maintain their functional pipeline, they find they can quickly address the needs of future clients without contorting their codebase too greatly.
00:17:30.000
The continuance of this functional approach leads to clarity, fewer public APIs, and less code overall.
00:17:44.000
In contrast to their object-oriented journey, they find that they maintain a smaller total number of lines of code despite handling more complex requirements as they progress.
00:18:04.000
They find far fewer complications arise over time, especially concerning public API management. The rapid development velocity and usage of data pipelines clearly contributes to being able to meet business goals.
00:18:25.000
Reflecting on their journey and its usefulness, they learn to be mindful of how changing requirements will affect the choices they make. They also discover that sprinkles of functional programming go a long way in enhancing their development practice.
00:18:43.000
In summary, this talk has been a journey of exploring the differences between object-oriented and functional programming styles, and how thoughtful choices can have a significant impact.
00:19:00.000
It's crucial for developers to consider how requirements might change over time and what paradigms best suit those changes, enhancing both productivity and the quality of the code delivered.
00:19:18.000
I hope you take away that consideration of business goals before starting any task can lead to a superb development process.
00:19:34.000
Finally, I encourage you after this talk to explore more resources on functional programming and consider how you'd like to bring in sprinkles of it into your Ruby development.
00:19:51.000
This can enhance your work and provide different perspectives on handling code. Thank you very much!