00:00:00.120
foreign
00:00:15.000
Good afternoon, everyone.
00:00:17.180
After starting to work with Ruby, one of my main objectives in life is to help people write better code and have more free time.
00:00:22.560
With that in mind, we need to take care of our code.
00:00:25.199
There is no magic trick to achieve this.
00:00:29.820
One of the best books I've read on the subject is 'Refactoring' by Martin Fowler.
00:00:34.260
It's not Ruby-specific, but after I read it, I decided to talk about it for the Ruby community.
00:00:36.360
There are many different refactoring patterns across various languages.
00:00:39.780
Sometimes, people refer to them by different names.
00:00:44.700
I prefer the names from Martin Fowler's book.
00:00:45.480
These names make sense in many languages, not just Ruby.
00:00:48.000
For example, you can change a function declaration, or collapse your class hierarchy.
00:00:51.440
If you have a superclass that shouldn't be a superclass of a certain class, you can modify that.
00:00:55.440
You can also extract a module, combining many functions into a module.
00:00:58.920
In Ruby, you can combine a lot of functions into a large class or decompose a conditional.
00:01:01.980
The names of these practices are pretty self-explanatory.
00:01:05.519
However, there is one type of refactoring I find challenging: the extract method.
00:01:09.320
I always feel the need to use an IDE, which I don't usually have when working with Ruby.
00:01:15.960
But I've discovered there's a plugin for VIM and one for Sublime Text that help with this.
00:01:20.420
The VIM plugin is much better, but both work.
00:01:25.860
Besides that, you basically copy, paste, and change variables, and that’s it.
00:01:29.280
For most refactoring tasks, this is sufficient.
00:01:31.140
But the challenging part is remembering when to do things and identifying problems.
00:01:35.640
People began using the term 'code smells' a few years ago to help identify issues in our code.
00:01:39.060
I think this idea is quite useful.
00:01:42.479
Let's start with this analogy.
00:01:44.700
Imagine visiting a marvelous country with beautiful beaches, but then you look at your code.
00:01:47.580
You check a new codebase and see there are no tests, leading you to think the original developers loved adventure.
00:01:51.840
Something is likely to break down the line.
00:01:55.319
There are lesser risks, like a simple case statement without an 'else', which can lead to code breaking in six months.
00:02:01.019
I have an example from my company where we had an enumeration for defining tag types.
00:02:06.840
All the current values were included in a case statement describing them with human-friendly names.
00:02:11.879
Six months later, when a reporter asked for a new tag type, we added an item to the DNM.
00:02:15.420
However, nobody remembered to increase the test cases to account for that.
00:02:19.200
As a result, we ended up with many unknown and new values exploding because there was no 'else' in the case.
00:02:21.920
So, that's one example.
00:02:28.260
Just a quick comment: if there are no tests, stop thinking about refactoring and focus on writing tests.
00:02:30.780
You won't be able to do anything effectively without tests.
00:02:33.420
To start, I want to list a few good practices for refactoring.
00:02:40.200
After completing refactoring, all your tests should still pass; otherwise, you may have broken something.
00:02:45.979
Refactoring, by definition, should not change the behavior of your code; you are improving it while keeping everything working as before.
00:02:50.700
In non-critical scenarios, you might change a class exposed as an API, but that’s not the ideal case.
00:02:55.800
Refactoring is about maintaining code quality, so you should allocate time for it in your daily work.
00:03:03.000
Each iteration, you should separate time for refactoring; if you take too long to improve your code, it will cost you much more later.
00:03:09.700
The time saved by improving code quality will also save your vacation time and reduce the number of bugs.
00:03:16.300
Since everyone likes to take time off, it makes sense to take care of your code.
00:03:20.740
As a golden rule, anything that doesn't simply improve the code should be handled in its own pull request.
00:03:23.340
If your code isn't properly organized, the people reviewing it will find it difficult to go through.
00:03:26.280
Instead of reviewing a hundred lines of feature-related code, they will review five thousand changed lines.
00:03:30.300
It's important to handle small refactorings always and leave the code you are touching in a better state than you found it.
00:03:35.979
Some problems seem simple but can lead to big issues.
00:03:40.000
For example, broken chains of 'safe' calls in Ruby may work, but they can break over time.
00:03:42.240
Similarly, a chain of three or four ternary operators might indicate that you missed one of the outcomes.
00:03:48.120
In general, this makes the code vulnerable to breaking.
00:03:53.579
Additionally, look out for things that don’t feel right.
00:03:57.999
This image references a show from a few years ago, illustrating how unexpected variables can appear.
00:04:00.300
If you encounter chemical case variables in your code, they probably don’t belong there.
00:04:05.400
For the first scenarios, several tools can help you address these issues.
00:04:10.000
For example, I suggest running RoboCop before you commit your code.
00:04:14.520
RoboCop verifies your code and can automatically handle quick fixes.
00:04:17.400
If you're using JavaScript, you can use 'lint-staged' to run it automatically before committing.
00:04:22.080
There are other code smells that are tolerable in an Italian restaurant but not in your code.
00:04:27.900
If your code looks like spaghetti, you really need to stop and fix something before it breaks.
00:04:31.500
Spaghetti code is usually problematic and doesn't look clean; it's often difficult to follow how it works.
00:04:36.000
In the best-case scenario, you'll notice that some classes are managing too many responsibilities.
00:04:39.000
When you have methods that are too large and do too many things, it’s time to refactor.
00:04:43.680
You may also face incomplete class libraries that could lead to circular dependencies.
00:04:48.180
Often the changes you make in one part of the code lead to unexpected breaks elsewhere.
00:04:52.740
This often resembles the IMAX code example where there's a warning for modifying code.
00:04:56.680
If you think you know what you're doing in such areas, beware and leave them alone.
00:05:01.200
These are examples of very well tested spaghetti code.
00:05:05.240
A significant problem associated with spaghetti code is the presence of 'whale classes'.
00:05:07.440
These are usually large classes with hundreds or even thousands of lines of code.
00:05:10.500
Such classes often indicate that they are performing too many functions.
00:05:14.160
Additionally, you may encounter 'doughnut methods' that are significant but not as big.
00:05:17.880
They possess high cyclomatic complexity and most of the time, they can be simplified.
00:05:21.060
For whale classes, if you identify multiple that have shared code, it’s possible to extract a superclass.
00:05:26.160
You can share that superclass across multiple classes or collapse the hierarchy to improve clarity.
00:05:31.200
If the class still feels like it should be doing more, consider combining functions into a module.
00:05:35.700
This will help isolate tests and keep your code cleaner and easier to read.
00:05:40.020
You can also move methods from larger classes to the new superclass or module.
00:05:44.760
Extract methods to reduce the size and complexity of the doughnut methods.
00:05:49.620
Turning variables into arguments simplifies code and helps to share code among methods.
00:05:54.000
This often occurs when the methods have small different variations.
00:05:56.760
To further improve readability, you can rename methods and variables for better clarity.
00:06:01.140
Sometimes, your code might feel like a fruit salad—lacking coherence.
00:06:05.940
A class with too many instance variables can lead to confusion.
00:06:10.000
If changing one variable breaks another property, that’s a red flag.
00:06:13.620
A class with too many methods can usually be solved by extracting a module.
00:06:16.920
You can simplify methods with excessive arguments by creating value objects.
00:06:20.760
Alternatively, you can pass a hash with possible values to the method.
00:06:24.480
This facilitates testing individual cases and achieves a cleaner layout.
00:06:30.360
Moreover, examining your code should not feel claustrophobic or overly complicated.
00:06:35.820
There are aspects in code that feel out of place.
00:06:37.740
For instance, unneeded comments like 'this code removes an item' can be misleading.
00:06:42.600
Instead, aim to explain the business rationale for the method's existence.
00:06:47.520
If you find yourself needing to explain exactly what a method does, reconsider the naming.
00:06:53.760
Comments that explain the business rationale are extremely useful.
00:06:56.880
I've encountered unnecessary libraries.
00:06:58.920
Once, I found an npm module that compared a value to itself—it seemed like a joke.
00:07:02.800
However, such modules unfortunately proliferate.
00:07:05.280
It's crucial to address situations where a method has multiple return statements.
00:07:08.400
Having excessive return points within a method leads to confusion and potential errors.
00:07:13.860
If you see numerous exit points in a method, consider breaking it into smaller functions.
00:07:17.400
Improving logic by using early conditions can also streamline your code.
00:07:22.020
This should result in simpler, more linear logic.
00:07:25.740
As I mentioned earlier, creating value objects in Ruby is infrequent.
00:07:29.340
You can often use a simple hash or create a struct.
00:07:31.320
You don't need to define a class with many accessors for variables.
00:07:35.340
structs create classes that grant basic functionality without additional overhead.
00:07:40.680
All these extraneous elements should simply be removed from your code.
00:07:44.980
You will notice areas of code that induce a sense of claustrophobia.
00:07:48.060
You feel it should be easier to make changes.
00:07:51.100
This tension arises frequently in spaghetti code or large classes.
00:07:56.160
For instance, having a single user class duplicated across multiple applications leads to complications.
00:08:00.140
Every modification necessitates updating all instances, which is impractical.
00:08:05.080
Encapsulating such shared logic into a separate service is usually a better approach.
00:08:10.980
You may find yourself needing to change various points in the code.
00:08:13.420
This increases complexity and can generate more problems than it resolves.
00:08:17.920
Next, let's discuss classic error pointers, such as fat controllers and thin models.
00:08:22.760
When your controllers are bloated with database-related code, it's a sign of trouble.
00:08:26.700
If a controller method has more than 50 or 100 lines, refactoring is necessary.
00:08:31.560
Controllers should delegate responsibilities to models, which also should not become bloated.
00:08:35.820
For instance, if you have a song model, it should only define its core attributes.
00:08:39.960
If it starts to include file conversion methods, that indicates a design flaw.
00:08:44.460
Such functionality should reside in separate services or libraries.
00:08:48.700
Fat models violate the principles of the Rails framework.
00:08:52.600
Ideally, all database-related interactions should occur within the models.
00:08:56.560
Controllers are meant to pass parameters into models and retrieve data for views.
00:09:00.280
This streamlined approach enhances testability and delineates responsibilities.
00:09:04.800
Moreover, certain elements may simply feel wrong or raise red flags.
00:09:09.000
For example, if modules are used in Rails migrations.
00:09:12.839
This creates complications should you need to reapply migrations across different databases.
00:09:16.680
I’ve faced this problem multiple times.
00:09:20.420
Migrations missing a down method complicate rollbacks.
00:09:23.520
Without the ability to revert migrations, you can’t update effectively.
00:09:27.179
These are clear indicators that problems will arise soon.
00:09:31.600
There are scenarios where certain practices lead to confusion.
00:09:35.880
For instance, if you have views overloaded with query logic or repetitive syntax.
00:09:40.020
In such cases, refactor your views by transferring complex code to view helpers.
00:09:44.420
This will yield cleaner views and simplify testing.
00:09:48.000
To summarize, tools like RoboCop or similar can greatly assist you.
00:09:52.020
By automating your process, your code quality will improve.
00:09:55.080
You'll also gain insights into areas that need improvement.
00:09:58.020
Separating refactoring from feature implementations leads to clearer pull requests.
00:10:01.560
Organizing your classes properly, utilizing superclass extractions, and restructuring methods can enhance clarity.
00:10:05.840
Using tools like RoboCop will highlight circular dependencies.
00:10:10.020
Pay attention to the size of controller and model class definitions and conduct thoughtful validation.
00:10:14.160
Always use view helpers judiciously.
00:10:16.560
Keep your views focused; avoid embedding complex queries.
00:10:20.220
Cleaner code results in fewer bugs and more free time to enjoy your vacation.
00:10:25.060
Thank you for your attention, and if you would like my contact information, it's available in the QR code.