00:00:16.720
Sorry everybody, I lost the slides and had to reconstruct them right now, just like in 10 minutes.
00:00:23.920
Ultra Light and Maintainable Rails Wizards. Who has written a wizard in their lifetime?
00:00:31.199
Okay, it’s almost like the most common web use case, yet it's the least appreciated when it comes to providing patterns for writing good code.
00:00:37.840
Many people struggle with writing maintainable wizards. A lot of the time, people write multi-step wizards where they end up doing a lot of copy-and-paste between the steps or across multiple controllers.
00:00:44.000
This makes it a significant problem for maintaining that code a year or two later. Every codebase is meant to be maintained for at least a year, and the maintenance cost is what's really expensive.
00:00:55.280
It's not just about writing a wizard in two weeks, but about whether I can maintain it cheaply over a year. That is really why I'm giving a talk on this subject.
00:01:06.080
So, let me give you an overview of what I'll cover: why we use wizards, an example of wizards, some implementation goals, the myriad wizard implementations out there, and finally, my recommended approach for creating ultra-light and maintainable wizards.
00:01:17.520
First of all, we don't want to overwhelm users with an extensive form of information, similar to those painful government forms we have in Canada.
00:01:28.000
Computers should enable people to make processes better than physical paper. One way to tackle this problem is to divide it into multiple steps in a wizard.
00:01:42.240
Additionally, it's about simplifying the workflow into digestible steps, similar to how you would enjoy a protein shake. Often, it gives you the opportunity to provide more explanation about what each form does by presenting more information across multiple pages, like what they do with TurboTax.
00:01:55.600
I should ask, who raised their hand to indicate they have experience with wizards?
00:02:03.440
I had a software architecture role at EarlyShares a couple of years ago where I helped launch their site. EarlyShares is similar to Kickstarter or Indiegogo, but focuses on allowing people to crowd-invest in businesses.
00:02:56.959
The website was built quickly to comply with new U.S. laws allowing crowd investment. As part of the website, they needed a couple of onboarding wizards—one for investors and one for business owners.
00:03:11.360
However, their business was bootstrapped, and we were only two developers: me as a senior and one junior developer, along with a CTO and a designer.
00:03:16.879
The CTO wanted us to move super fast, and I was brought in as their Rails expert. Interestingly, I had not written a wizard in about four or five years—since my Java development days.
00:03:37.760
So, when I started tackling this problem in Ruby, I checked Google guides and Stack Overflow, but none satisfied me. Now, let’s discuss what I found.
00:03:53.760
The basic wizard example consisted of four steps: Step one, collect basic info; Step two, input details; Step three, upload document content; and Step four, preview before finishing the wizard.
00:04:06.239
Once completed, it presents a summary like a landing page for the proposed business investment.
00:04:22.240
The goals I aimed for were to ensure that the Rails server persisted progress at each step without relying on JavaScript client-side tricks. I also wanted the implementation to be RESTful, which is a common challenge when building wizards.
00:04:40.480
Moreover, I aimed to stick to MVC and object-oriented principles because we wanted to ensure that the code remained maintainable for future developers.
00:04:48.799
Additional non-functional requirements included productivity, as the CTO emphasized the need to move quickly. But I tend to pay attention to details and design concerns, which could slow things down for good reasons.
00:05:01.360
Ultimately, the story had a happy ending with the project achieving maintainability for both junior and senior developers, including a senior developer in Brazil who joined later.
00:05:14.960
Performance and security concerns were also top of mind, which are basic considerations whenever building any features.
00:05:31.680
One approach I saw was to create one controller per wizard step, thus creating a REST resource for each step.
00:05:42.240
This resulted in multiple controllers, sets of views, and helpers, where each controller redirects to the next. For validations, you could implement either a single ActiveRecord with conditional validations or multiple ActiveRecords.
00:06:00.960
So, I ask, who here could find concerns with this approach or suggest improvements?
00:06:06.960
A volunteer noted that managing a multitude of controllers could pose a challenge.
00:06:15.840
This raises the issue of repetition; a lot of code ended up being repetitive across the controllers.
00:06:26.079
For example, loading the same resource and running validations resulted in a lot of duplicated code. A previous developer had tried to implement a feature after I joined the team and found it took months to accomplish, as they grappled with the original design.
00:06:55.360
This highlights the importance of a well-structured development approach to enable quicker implementation of features. My aim was to employ the Ultra Light Maintainable Wizard approach I had discovered earlier.
00:07:14.559
This method proved effective, allowing us to develop within seven days, with tests prioritized early in the process.
00:07:20.960
We achieved this because we no longer had so many controllers, which resulted in less code and therefore reduced testing requirements.
00:07:28.960
Let’s discuss another approach—using one controller for all wizard steps.
00:07:34.960
This approach still relied on ActiveRecords, but could lead to repetitive code across actions.
00:07:42.000
It was an improvement over the first approach but still had serious shortcomings, mainly the repetitiveness in code.
00:07:51.680
Using presenters, an abstraction layer between ActiveRecord and the controller, can be beneficial to have separate validations for each wizard step.
00:08:00.720
But it still presented challenges, as the controller often ends up repeating similar validations.
00:08:10.080
Now, who here has implemented a wizard with session accumulation? How did that work out for you?
00:08:25.760
One participant shared their experience, mentioning difficulties managing session information across multiple controllers.
00:08:35.200
Indeed, reliance on session storage can lead to complexity in controller code, breaking the MVC model.
00:08:45.840
It makes managing validations complicated and can lead to duplicated code.
00:08:54.080
Hidden value accumulation was another method, where values were hidden into fields on the submission page.
00:09:05.760
While this method is stateless and avoids session issues, it can expose sensitive user information unnecessarily.
00:09:15.360
Using a state machine can address some challenges of wizard workflows; however, it comes with its own issues.
00:09:26.840
You must integrate validations directly connected to different states, which can complicate the model.
00:09:36.080
But in general, a state machine can offer a better compromise over other wizard implementations.
00:09:52.560
Conditional validations arise when there is a need to validate based on the current step.
00:10:05.920
The approach has merits, but with the added complexity, it risks obscuring business logic.
00:10:19.600
The idea should focus on making the user's experience seamless and easy to understand. For that reason, keeping concerns separate is key.
00:10:33.440
This way, one can avoid overwhelming the model or the controller with mixed responsibilities.
00:10:40.480
There are various gems available that can assist in simplifying wizard creation.
00:10:49.040
However, they may not effectively meet all goals related to RESTful design, MVC and maintainability.
00:11:06.159
Now, let’s subject the wizard process to a comprehensive review.
00:11:20.000
So, I thought, let’s attempt to solve this from scratch using object-oriented principles and domain-driven design.
00:11:30.000
Who here is familiar with 'Domain-Driven Design'? It’s a great resource for learning how to approach real business problems with object-oriented design.
00:11:48.680
Refocusing on wizards, their highest goal from the user perspective should always be to streamline workflow.
00:12:04.639
Yes, to simplify and present information in an organized fashion.
00:12:08.480
The overall goal of a wizard is to produce an object through a sequence of steps—almost like the builder design pattern.
00:12:25.000
Each step of the wizard is essentially a view of a part of the larger object being created.
00:12:39.880
This can be very straightforward within REST resources, as it separates concerns effectively.
00:12:51.420
For instance, a wizard step can be made a nested resource of a wider object, thus simplifying routing.
00:13:04.080
Furthermore, this design avoids conditional validations, which complicate models and make them harder to read.
00:13:19.440
Instead, separating validations and associated logic within the presenter enforces a clearer structure.
00:13:30.000
Less embedded logic ensures that the code remains clean and maintainable over time.
00:13:39.920
Each wizard step is designed as a separate presenter—each handling only its validation and associated behavior.
00:13:55.920
This encapsulates related concerns and isolates each step, reducing the chances of an unwieldy codebase.
00:14:07.920
Having a project controller that oversees the wizard process provides an avenue for control without overloading responsibilities.
00:14:21.920
By following these principles, we can create ultra-light and maintainable wizards that promote developer efficiency.
00:14:35.920
Thus, as you develop your wizards, remember to prioritize clarity and separation of concerns.
00:14:46.000
In conclusion, I discussed why to use wizards, provided an example, outlined implementation goals, and concluded with my approach.
00:14:56.000
My goal is to ensure that the code is not only maintainable today but also a year from now while minimizing training efforts.
00:15:07.120
Please feel free to connect with me, and thank you for your attention.
00:15:15.960
I'm looking forward to monitoring progress on projects, and I'm here to answer any questions.
00:15:28.000
Once again, my name is Andy Maleh, VP of Engineering at Big Astronaut.
00:15:39.320
We’re a fully remote consulting company, and we're currently hiring. Thank you, everyone!